mirror of https://github.com/buster-so/buster.git
report final update fix
This commit is contained in:
commit
0a430bef5b
|
@ -104,7 +104,7 @@ Only utilize the required/default fields unless the user specifically requests t
|
|||
# style: percent
|
||||
# replaceMissingDataWith: 0
|
||||
# numberSeparatorStyle: ','
|
||||
# multiplier: 100 # if calculated and not already multiplied
|
||||
# multiplier: 100 # Use 100 if SQL returns 0.75 for 75%; use 1 if SQL returns 75 for 75%
|
||||
# minimumFractionDigits: 1
|
||||
# maximumFractionDigits: 2
|
||||
# -------------------------------------
|
||||
|
@ -348,15 +348,21 @@ definitions:
|
|||
type: string
|
||||
enum:
|
||||
- currency # Note: The "$" sign is automatically prepended.
|
||||
- percent # Note: "%" sign is appended. For percentage values:
|
||||
# - If the value comes directly from a database column, use multiplier: 1
|
||||
# - If the value is calculated in your SQL query and not already multiplied by 100, use multiplier: 100
|
||||
- percent # Note: "%" sign is automatically appended as a suffix.
|
||||
# IMPORTANT: You MUST analyze the SQL query to determine the correct multiplier:
|
||||
# - If SQL returns decimal ratios (e.g., 0.75 for 75%), use multiplier: 100
|
||||
# - If SQL returns percentage values (e.g., 75 for 75%), use multiplier: 1 (or omit it)
|
||||
- number
|
||||
- date # Note: For date columns, consider setting xAxisTimeInterval in xAxisConfig to control date grouping (day, week, month, quarter, year)
|
||||
- string
|
||||
multiplier:
|
||||
type: number
|
||||
description: Value to multiply the number by before display. Default value is 1. For percentages, the multiplier depends on how the data is sourced: if the value comes directly from a database column, use multiplier: 1; if the value is calculated in your SQL query and not already multiplied by 100, use multiplier: 100.
|
||||
description: |
|
||||
Value to multiply the number by before display. Default: 1.
|
||||
For percentages with style: percent, you MUST examine the SQL query and data format:
|
||||
- If SQL returns decimal ratios (e.g., 0.75), use multiplier: 100 to convert to percentage (75%)
|
||||
- If SQL returns percentage values (e.g., 75), use multiplier: 1 or omit this field
|
||||
The agent must analyze the actual SQL query and understand the data format, not make assumptions.
|
||||
displayName:
|
||||
type: string
|
||||
description: Custom display name for the column
|
||||
|
|
|
@ -312,35 +312,39 @@ export function createModifyReportsDelta(context: ModifyReportsContext, state: M
|
|||
},
|
||||
};
|
||||
|
||||
// Update the database with the result of all edits using Promise chain
|
||||
// Update the database with the result of all edits (concurrent, not chained)
|
||||
try {
|
||||
// Chain this write to ensure sequential execution
|
||||
state.lastDbWritePromise = (async () => {
|
||||
// Wait for any previous write to complete
|
||||
if (state.lastDbWritePromise) {
|
||||
try {
|
||||
await state.lastDbWritePromise;
|
||||
} catch (error) {
|
||||
// Previous write failed, but we continue
|
||||
console.warn('[modify-reports-delta] Previous write failed:', error);
|
||||
}
|
||||
}
|
||||
// Initialize pending writes array if needed
|
||||
if (!state.pendingDbWrites) {
|
||||
state.pendingDbWrites = [];
|
||||
}
|
||||
|
||||
// Now do our write
|
||||
// We're already inside a check for state.reportId being defined
|
||||
if (!state.reportId) {
|
||||
throw new Error('Report ID is unexpectedly undefined');
|
||||
}
|
||||
return batchUpdateReport({
|
||||
reportId: state.reportId,
|
||||
content: currentContent,
|
||||
name: state.reportName || undefined,
|
||||
versionHistory,
|
||||
// We're already inside a check for state.reportId being defined
|
||||
if (!state.reportId) {
|
||||
throw new Error('Report ID is unexpectedly undefined');
|
||||
}
|
||||
|
||||
// Create the write promise and add to pending writes
|
||||
const writePromise = batchUpdateReport({
|
||||
reportId: state.reportId,
|
||||
content: currentContent,
|
||||
name: state.reportName || undefined,
|
||||
versionHistory,
|
||||
})
|
||||
.then(() => {
|
||||
// Convert to void to match the array type
|
||||
return;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('[modify-reports-delta] Database write failed:', error);
|
||||
throw error;
|
||||
});
|
||||
})();
|
||||
|
||||
// Await the promise to handle errors and ensure completion
|
||||
await state.lastDbWritePromise;
|
||||
// Add to pending writes array
|
||||
state.pendingDbWrites.push(writePromise);
|
||||
|
||||
// Await this specific write to handle errors
|
||||
await writePromise;
|
||||
|
||||
// No cache update during delta - execute will handle write-through
|
||||
|
||||
|
|
|
@ -177,16 +177,22 @@ async function processEditOperations(
|
|||
|
||||
// Write all changes to database in one operation
|
||||
try {
|
||||
// Wait for any pending delta writes to complete before doing final update
|
||||
if (state?.lastDbWritePromise) {
|
||||
console.info('[modify-reports-execute] Waiting for pending delta writes to complete');
|
||||
// Wait for ALL pending delta writes to complete before doing final update
|
||||
if (state?.pendingDbWrites && state.pendingDbWrites.length > 0) {
|
||||
console.info(
|
||||
`[modify-reports-execute] Waiting for ${state.pendingDbWrites.length} pending delta writes to complete`
|
||||
);
|
||||
try {
|
||||
await state.lastDbWritePromise;
|
||||
// Wait for all writes to complete (some may fail, that's OK)
|
||||
await Promise.allSettled(state.pendingDbWrites);
|
||||
// Add small delay to ensure we're absolutely last
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
console.info(
|
||||
'[modify-reports-execute] All delta writes completed, proceeding with final update'
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
'[modify-reports-execute] Delta write failed, proceeding with final update:',
|
||||
'[modify-reports-execute] Error waiting for delta writes, proceeding with final update:',
|
||||
error
|
||||
);
|
||||
}
|
||||
|
|
|
@ -103,9 +103,9 @@ export type ModifyReportsOutput = z.infer<typeof ModifyReportsOutputSchema>;
|
|||
export type ModifyReportsContext = z.infer<typeof ModifyReportsContextSchema>;
|
||||
export type ModifyReportsEditState = z.infer<typeof ModifyReportsEditStateSchema>;
|
||||
|
||||
// Extend the inferred type to include Promise field (not supported by Zod directly)
|
||||
// Extend the inferred type to include Promise fields (not supported by Zod directly)
|
||||
export type ModifyReportsState = z.infer<typeof ModifyReportsStateSchema> & {
|
||||
lastDbWritePromise?: Promise<void>;
|
||||
pendingDbWrites?: Promise<void>[]; // Track all pending writes
|
||||
};
|
||||
|
||||
// Factory function that accepts agent context and maps to tool context
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||
"extends": ["../../biome.json"],
|
||||
"files": {
|
||||
"include": ["src/**/*", "scripts/**/*"]
|
||||
"include": ["src/**/*"],
|
||||
"ignore": ["scripts/**/*"]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
|
|
|
@ -60,29 +60,12 @@ export function initializePool<T extends Record<string, postgres.PostgresType>>(
|
|||
}
|
||||
|
||||
// Create postgres client with pool configuration
|
||||
// Disable SSL for local development
|
||||
const isDevelopment =
|
||||
process.env.ENVIRONMENT === 'development' || process.env.NODE_ENV === 'development';
|
||||
|
||||
console.log(
|
||||
'Database connection - ENVIRONMENT:',
|
||||
process.env.ENVIRONMENT,
|
||||
'NODE_ENV:',
|
||||
process.env.NODE_ENV,
|
||||
'isDevelopment:',
|
||||
isDevelopment
|
||||
);
|
||||
|
||||
// SSL is controlled via the connection string (e.g., ?sslmode=require)
|
||||
globalPool = postgres(connectionString, {
|
||||
max: poolSize,
|
||||
idle_timeout: 30,
|
||||
connect_timeout: 30,
|
||||
prepare: true,
|
||||
ssl: isDevelopment
|
||||
? false
|
||||
: {
|
||||
rejectUnauthorized: false, // Allow self-signed certificates
|
||||
},
|
||||
...config,
|
||||
});
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ vi.mock('../../connection', () => ({
|
|||
|
||||
// Mock drizzle-orm
|
||||
vi.mock('drizzle-orm', async (importOriginal) => {
|
||||
const actual = (await importOriginal()) as any;
|
||||
const actual = (await importOriginal()) as Record<string, unknown>;
|
||||
return {
|
||||
...actual,
|
||||
sql: actual.sql,
|
||||
|
|
|
@ -10,7 +10,7 @@ vi.mock('../../connection', () => ({
|
|||
|
||||
// Mock drizzle-orm
|
||||
vi.mock('drizzle-orm', async (importOriginal) => {
|
||||
const actual = (await importOriginal()) as any;
|
||||
const actual = (await importOriginal()) as Record<string, unknown>;
|
||||
return {
|
||||
...actual,
|
||||
sql: actual.sql,
|
||||
|
|
|
@ -82,10 +82,10 @@ describe('getOrganizationDocs', () => {
|
|||
organizationId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
};
|
||||
|
||||
let mockSelect: any;
|
||||
let mockFrom: any;
|
||||
let mockWhere: any;
|
||||
let mockOrderBy: any;
|
||||
let mockSelect: ReturnType<typeof vi.fn>;
|
||||
let mockFrom: ReturnType<typeof vi.fn>;
|
||||
let mockWhere: ReturnType<typeof vi.fn>;
|
||||
let mockOrderBy: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
|
|
@ -124,10 +124,10 @@ describe('upsertDoc', () => {
|
|||
organizationId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
};
|
||||
|
||||
let mockInsert: any;
|
||||
let mockValues: any;
|
||||
let mockOnConflictDoUpdate: any;
|
||||
let mockReturning: any;
|
||||
let mockInsert: ReturnType<typeof vi.fn>;
|
||||
let mockValues: ReturnType<typeof vi.fn>;
|
||||
let mockOnConflictDoUpdate: ReturnType<typeof vi.fn>;
|
||||
let mockReturning: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
|
|
@ -87,9 +87,10 @@ export async function createShortcut(input: CreateShortcutInput) {
|
|||
.returning();
|
||||
|
||||
return created;
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
// Handle unique constraint violation
|
||||
if (error?.code === '23505' && error?.constraint?.includes('unique')) {
|
||||
const dbError = error as { code?: string; constraint?: string };
|
||||
if (dbError?.code === '23505' && dbError?.constraint?.includes('unique')) {
|
||||
throw new Error(`Shortcut with name '${validated.name}' already exists`);
|
||||
}
|
||||
throw error;
|
||||
|
|
|
@ -16,7 +16,7 @@ export type UpdateShortcutInput = z.infer<typeof UpdateShortcutInputSchema>;
|
|||
export async function updateShortcut(input: UpdateShortcutInput) {
|
||||
const validated = UpdateShortcutInputSchema.parse(input);
|
||||
|
||||
const updateData: Record<string, any> = {
|
||||
const updateData: Record<string, unknown> = {
|
||||
updatedBy: validated.updatedBy,
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue