Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@
"@nestjs/typeorm": "^10.0.2",
"@types/bcrypt": "^5.0.2",
"@types/fs-extra": "^11.0.4",
"@types/normalize-path": "^3.0.2",
"bcrypt": "^5.1.1",
"class-validator": "^0.14.1",
"fs-extra": "^11.2.0",
"graphql": "^16.9.0",
"graphql-subscriptions": "^2.0.0",
"graphql-ws": "^5.16.0",
"markdown-to-txt": "^2.0.1",
"normalize-path": "^3.0.0",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"sqlite3": "^5.1.7",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BuilderContext } from 'src/build-system/context';
import { DBSchemaHandler } from '../node/database-schemas/schemas';
import { DBSchemaHandler } from '../handlers/database/schemas/schemas';
import { readFileSync } from 'fs';
import markdownToTxt from 'markdown-to-txt';

Expand Down Expand Up @@ -33,7 +33,7 @@
);
const plainText = markdownToTxt(mdFileContent);
const result = await handler.run(context, plainText);
console.log(result);

Check warning on line 36 in backend/src/build-system/__tests__/test-database-schemas.spec.ts

View workflow job for this annotation

GitHub Actions / autofix

Unexpected console statement
}, 30000);
},
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import { FileGeneratorHandler } from '../node/file-generate'; // Update with actual file path to the handler
import { FileGeneratorHandler } from '../handlers/file-generate'; // Update with actual file path to the handler

describe('FileGeneratorHandler', () => {
const projectSrcPath = 'src\\build-system\\__tests__\\test-project\\';
Expand Down
173 changes: 173 additions & 0 deletions backend/src/build-system/__tests__/test.backend-code-generator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/* eslint-disable no-console */
import { BuilderContext } from 'src/build-system/context';
import { BuildResult, BuildSequence } from '../types';
import { BuildSequenceExecutor } from '../executor';
import * as fs from 'fs';
import * as path from 'path';

describe('Sequence: PRD -> UXSD -> UXDD -> UXSS -> DBSchemas -> BackendCodeGenerator', () => {
// Generate a unique folder with a timestamp
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const logFolderPath = `./logs/backend_code_generator-${timestamp}`;
fs.mkdirSync(logFolderPath, { recursive: true });

/**
* Utility function to extract content within <GENERATE> tags and write to .md files.
* @param handlerName - The name of the handler/node.
* @param data - The data returned by the handler/node.
*/
const writeMarkdownToFile = (handlerName: string, data: BuildResult) => {
try {
// Extract "data" field and ensure it's a string
const content: string = data?.data;
if (typeof content !== 'string') {
throw new Error(`Invalid data format for handler: ${handlerName}`);
}

const sanitizedHandlerName = handlerName.replace(/[^a-zA-Z0-9_-]/g, '_');
const filePath = path.join(logFolderPath, `${sanitizedHandlerName}.md`);
fs.writeFileSync(filePath, content, 'utf8');
console.log(`Logged ${handlerName} result data to ${filePath}`);
} catch (error) {
console.error(`Failed to write markdown for ${handlerName}:`, error);
throw error;
}
};

it('should execute the backend code generation sequence and log results to individual files', async () => {
// Define the build sequence up to Backend Code Generator
const sequence: BuildSequence = {
id: 'test-backend-sequence',
version: '1.0.0',
name: 'Test PRD to Backend Code Generation Sequence',
description:
'Testing sequence execution from PRD to Backend Code Generation',
steps: [
{
id: 'step-1',
name: 'Generate PRD',
nodes: [
{
id: 'op:PRD::STATE:GENERATE',
name: 'PRD Generation Node',
type: 'ANALYSIS',
subType: 'PRD',
},
],
},
{
id: 'step-2',
name: 'Generate UX Sitemap Document',
nodes: [
{
id: 'op:UXSMD::STATE:GENERATE',
name: 'UX Sitemap Document Node',
type: 'UX',
subType: 'SITEMAP',
requires: ['op:PRD::STATE:GENERATE'],
},
],
},
{
id: 'step-3',
name: 'Generate UX Data Map Document',
nodes: [
{
id: 'op:UX_DATAMAP::STATE:GENERATE',
name: 'UX Data Map Document Node',
type: 'UX',
subType: 'DATAMAP',
requires: ['op:UXSMD::STATE:GENERATE'],
},
],
},
{
id: 'step-4',
name: 'Generate Database Requirements',
nodes: [
{
id: 'op:DATABASE_REQ::STATE:GENERATE',
name: 'Database Requirements Node',
type: 'DATABASE',
subType: 'SCHEMAS',
requires: ['op:UX_DATAMAP::STATE:GENERATE'],
},
],
},
{
id: 'step-5',
name: 'Generate Database Schemas',
nodes: [
{
id: 'op:DATABASE:SCHEMAS',
name: 'Database Schemas Node',
type: 'DATABASE',
subType: 'SCHEMAS',
requires: ['op:DATABASE_REQ::STATE:GENERATE'],
},
],
},
{
id: 'step-6',
name: 'Generate Backend Code',
nodes: [
{
id: 'op:BACKEND_CODE::STATE:GENERATE',
name: 'Backend Code Generator Node',
type: 'BACKEND',
requires: [
'op:DATABASE:SCHEMAS',
'op:UX_DATAMAP::STATE:GENERATE',
],
},
],
},
],
};
Comment on lines +37 to +126
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Validation is indeed missing in critical sequence steps

The codebase inspection reveals that validation is currently minimal:

  • The DBSchemaHandler lacks schema validation mechanisms
  • The BackendCodeHandler only has a comment suggesting optional validation
  • No structured validation is implemented for the generated artifacts

Key locations requiring validation:

  • backend/src/build-system/handlers/database/schemas/schemas.ts: Add schema validation for generated database schemas
  • backend/src/build-system/handlers/backend/code-generate/index.ts: Implement validation for generated backend code
🔗 Analysis chain

Consider adding validation for critical sequence steps.

While the sequence is well-structured, consider adding validation for critical steps, especially for database schema generation and backend code generation steps, to ensure the generated artifacts meet the expected format and structure.


Let me analyze the broader context of handler implementations and validation mechanisms in the codebase to provide a more accurate assessment of the review comment's concern about validation.


Let me analyze the actual implementations of these critical handlers to determine if the review comment's concern about validation is already addressed or if it's indeed missing.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the handlers referenced in the sequence exist in the codebase
echo "Verifying handlers existence..."
handlers=(
  "PRD"
  "UXSMD"
  "UX_DATAMAP"
  "DATABASE_REQ"
  "DATABASE:SCHEMAS"
  "BACKEND_CODE"
)

for handler in "${handlers[@]}"; do
  echo "Searching for handler: $handler"
  rg -l "class.*Handler.*${handler}" -g "*.ts"
done

Length of output: 1103


Script:

#!/bin/bash
# Let's try a broader search for handlers and processors
echo "Searching for handler implementations..."
rg -l "Handler|Processor" -g "*.ts"

echo -e "\nSearching for sequence step implementations..."
# Look for files that might contain sequence step implementations
rg -l "type.*=.*'(PRD|UX|DATABASE|BACKEND)'" -g "*.ts"

echo -e "\nSearching for validation implementations..."
# Look for validation related code
rg -l "validate|validation" -g "*.ts" --type typescript

Length of output: 1896


Script:

#!/bin/bash
# Let's check the implementation of handlers to see if validation is already in place
echo "Checking database schema handler implementation..."
rg -A 10 "class.*Handler" backend/src/build-system/handlers/database/schemas/schemas.ts

echo -e "\nChecking backend code generator handler implementation..."
rg -A 10 "class.*Handler" backend/src/build-system/handlers/backend/code-generate/index.ts

echo -e "\nChecking for existing validation mechanisms..."
rg "validate|validation" backend/src/build-system/handlers/database/schemas/schemas.ts backend/src/build-system/handlers/backend/code-generate/index.ts

Length of output: 1943


// Initialize the BuilderContext with the defined sequence and environment
const context = new BuilderContext(sequence, 'test-env');

// Set input data for context
context.setData('projectName', 'Spotify-like Music Web');
context.setData('description', 'Users can play music');
context.setData('platform', 'web');
context.setData('databaseType', 'SQLite'); // Can be 'PostgreSQL', 'MongoDB', etc., based on your needs

try {
// Execute the build sequence
await BuildSequenceExecutor.executeSequence(sequence, context);

// Iterate through each step and node to retrieve and log results
for (const step of sequence.steps) {
for (const node of step.nodes) {
const resultData = await context.getResult(node.id);
console.log(`Result for ${node.name}:`, resultData);

if (resultData && resultData.success) {
writeMarkdownToFile(node.name, resultData);
} else if (resultData && !resultData.success) {
console.error(
`Handler ${node.name} failed with error:`,
resultData.error,
);
// Optionally, you can log this to a separate file or handle it as needed
}
}
}

console.log(
'Sequence executed successfully. Logs stored in:',
logFolderPath,
);
} catch (error) {
console.error('Error during sequence execution:', error);
fs.writeFileSync(
path.join(logFolderPath, 'error.txt'),
`Error: ${error.message}\n${error.stack}`,
'utf8',
);
throw new Error('Sequence execution failed.');
}
}, 600000); // Timeout set to 10 minutes
});
2 changes: 1 addition & 1 deletion backend/src/build-system/__tests__/test.file-arch.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BuilderContext } from 'src/build-system/context';
import { FileArchGenerateHandler } from '../node/file-arch';
import { FileArchGenerateHandler } from '../handlers/file-arch';
import markdownToTxt from 'markdown-to-txt';
import { readFileSync } from 'fs-extra';

Expand Down
2 changes: 1 addition & 1 deletion backend/src/build-system/__tests__/test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { BuilderContext } from '../context';
import { BuildSequenceExecutor } from '../executor';
import { BuildHandlerManager } from '../hanlder-manager';
import { ProjectInitHandler } from '../node/project-init';
import { ProjectInitHandler } from '../handlers/project-init';
import { BuildSequence } from '../types';
describe('Project Init Handler Test', () => {
let context: BuilderContext;
Expand Down
110 changes: 110 additions & 0 deletions backend/src/build-system/handlers/backend/code-generate/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { BuildHandler, BuildResult } from 'src/build-system/types';
import { BuilderContext } from 'src/build-system/context';
import { generateBackendCodePrompt } from './prompt';
import { Logger } from '@nestjs/common';
import {
parseGenerateTag,
removeCodeBlockFences,
} from 'src/build-system/utils/database-utils';

/**
* Defines the expected order and types of arguments for BackendCodeHandler.
*
* @param sitemapDoc - The sitemap documentation as a string.
* @param DatamapDoc - The data analysis document as a string.
Comment on lines +13 to +14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use consistent camelCase naming for 'datamapDoc'

The parameter 'DatamapDoc' should be renamed to 'datamapDoc' to follow camelCase naming conventions and maintain consistency throughout the code.

Apply this diff to rename the parameter and its usages:

 /** 
- * @param DatamapDoc - The data analysis document as a string.
+ * @param datamapDoc - The data analysis document as a string.
  * @param currentFile - (Optional) The name of the current file. Defaults to 'backend.js'.
  * @param dependencyFile - (Optional) The name of the dependency file. Defaults to 'dependencies.json'.
  */
-type BackendCodeArgs = [
-  sitemapDoc: string,
-  DatamapDoc: string,
+type BackendCodeArgs = [
+  sitemapDoc: string,
+  datamapDoc: string,
   currentFile?: string,
   dependencyFile?: string,
 ];

 // Destructure arguments with default values for optional parameters
 const [
   sitemapDoc,
-  DatamapDoc,
+  datamapDoc,
   currentFile = 'backend.js',
   dependencyFile = 'dependencies.json',
 ] = args as BackendCodeArgs;

 // Generate the prompt using the provided documents and project name
 const backendCodePrompt = generateBackendCodePrompt(
   projectName,
   sitemapDoc,
-  DatamapDoc,
+  datamapDoc,
   currentFile,
   dependencyFile,
 );

Also applies to: 18-23, 55-60, 70-70

* @param currentFile - (Optional) The name of the current file. Defaults to 'backend.js'.
* @param dependencyFile - (Optional) The name of the dependency file. Defaults to 'dependencies.json'.
*/
type BackendCodeArgs = [
sitemapDoc: string,
DatamapDoc: string,
currentFile?: string,
dependencyFile?: string,
];

/**
* BackendCodeHandler is responsible for generating the backend codebase
* based on the provided sitemap and data mapping documents.
*/
export class BackendCodeHandler implements BuildHandler {
readonly id = 'op:BACKEND_CODE::STATE:GENERATE';
readonly logger: Logger = new Logger('BackendCodeHandler');

/**
* Executes the handler to generate backend code.
* @param context - The builder context containing configuration and utilities.
* @param args - The variadic arguments required for generating the backend code.
* @returns A BuildResult containing the generated code and related data.
*/
async run(context: BuilderContext, ...args: any[]): Promise<BuildResult> {
this.logger.log('Generating Backend Codebase...');

// Retrieve projectName from context
const projectName =
context.getData('projectName') || 'Default Project Name';
this.logger.debug(`Project Name: ${projectName}`);
const databaseType =
context.getData('databaseType') || 'Default database type';
this.logger.debug(`Database Type: ${databaseType}`);

// Validate and extract args
if (!args || !Array.isArray(args)) {
throw new Error(
'Backend code generation requires specific configuration arguments as an array.',
);
}

// Destructure arguments with default values for optional parameters
const [
sitemapDoc,
DatamapDoc,
currentFile = 'backend.js',
dependencyFile = 'dependencies.json',
] = args as BackendCodeArgs;
Comment on lines +58 to +63
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Validate required arguments before proceeding

Ensure that the required arguments sitemapDoc and datamapDoc are provided before destructuring to prevent runtime errors.

Apply this diff to add validation:

+ // Validate required arguments
+ if (!sitemapDoc || !datamapDoc) {
+   throw new Error('sitemapDoc and datamapDoc are required arguments.');
+ }

Committable suggestion skipped: line range outside the PR's diff.


this.logger.debug(
'Sitemap Documentation and Data Analysis Document are provided.',
);

// Generate the prompt using the provided documents and project name
const backendCodePrompt = generateBackendCodePrompt(
projectName,
sitemapDoc,
DatamapDoc,
databaseType,
currentFile,
dependencyFile,
);

// Log the prompt generation
this.logger.debug('Generated backend code prompt.');

try {
// Invoke the language model to generate the backend code
const modelResponse = await context.model.chatSync(
{
content: backendCodePrompt,
},
'gpt-4o-mini', // Specify the model variant as needed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Make model name configurable

The model name 'gpt-4o-mini' is hardcoded. Consider making it configurable through the context or environment variables.

- 'gpt-4o-mini', // Specify the model variant as needed
+ context.getData('modelName') || 'gpt-4o-mini', // Use configured model or fallback

Committable suggestion skipped: line range outside the PR's diff.

);

const generatedCode = removeCodeBlockFences(
parseGenerateTag(modelResponse),
);

// Optionally, you can process or validate the generated code here
this.logger.debug('Backend code generated and parsed successfully.');

return {
success: true,
data: generatedCode,
};
} catch (error) {
this.logger.error('Error during backend code generation:', error);
return {
success: false,
error: new Error('Failed to generate backend code.'),
};
}
}
}
Loading
Loading