Skip to content

Copy-and-Paste 2: Basic copy and paste task#147

Merged
tnaum-ms merged 26 commits into
feature/copy-and-pastefrom
dev/xingfan/111-copy-and-paste-2-implement-basic-copy-and-paste-task
Aug 13, 2025
Merged

Copy-and-Paste 2: Basic copy and paste task#147
tnaum-ms merged 26 commits into
feature/copy-and-pastefrom
dev/xingfan/111-copy-and-paste-2-implement-basic-copy-and-paste-task

Conversation

@tnaum-ms

@tnaum-ms tnaum-ms commented Jun 10, 2025

Copy link
Copy Markdown
Collaborator

Closes #111

@tnaum-ms tnaum-ms linked an issue Jun 10, 2025 that may be closed by this pull request
@tnaum-ms tnaum-ms requested a review from Copilot June 10, 2025 07:17

This comment was marked as outdated.

tnaum-ms and others added 4 commits June 10, 2025 09:59
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…-documentdb into dev/xingfan/111-copy-and-paste-2-implement-basic-copy-and-paste-task
…into dev/xingfan/111-copy-and-paste-2-implement-basic-copy-and-paste-task
@tnaum-ms

tnaum-ms commented Jun 18, 2025

Copy link
Copy Markdown
Collaborator Author

@xingfan-git look at the new additions to our base feature-branch, the Task API is now simpler to use, and, progress monitoring is in place ( #114 ) . I didn't have enough time to implement the fancy one - it's in the queue for later ( #182 ).

There is a test command in there in the feature branch, just open the command palette and search for demo. You'll see the DemoTask in action with its progress reporting.

Recording.2025-06-18.192942.mp4

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements a basic copy-paste functionality for MongoDB collections, allowing users to copy documents from one collection to another. The implementation follows a database-agnostic design pattern using interfaces for document reading and writing operations.

Key changes:

  • Adds copy-paste utility interfaces and types for generic document operations
  • Implements a task-based copy-paste collection feature with memory-efficient streaming
  • Provides MongoDB-specific implementations for document reading and writing operations

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/utils/copyPasteUtils.ts Defines interfaces and types for copy-paste operations including conflict resolution strategies
src/services/tasks/CopyPasteCollectionTask.ts Implements the main task logic for copying collections with buffer-based streaming
src/documentdb/DocumentProvider.ts MongoDB-specific implementations of DocumentReader and DocumentWriter interfaces
src/documentdb/ClustersClient.ts Adds transaction support and helper methods for MongoDB operations
src/commands/copyCollection/copyCollection.ts Simple command to mark a collection for copying
src/commands/pasteCollection/pasteCollection.ts Command to execute the copy-paste operation with user confirmation
src/commands/importDocuments/importDocuments.ts Updates to use new MongoDB error handling patterns
src/extensionVariables.ts Adds temporary storage for copied collection reference
package.json Registers new copy and paste collection commands
l10n/bundle.l10n.json Adds localization strings for the new functionality

Comment thread src/extensionVariables.ts

// TODO: TN improve this: This is a temporary solution to get going.
export let copiedCollectionNode: CollectionItem | undefined;

Copilot AI Aug 12, 2025

Copy link

Choose a reason for hiding this comment

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

This TODO comment indicates technical debt. Consider implementing a more robust clipboard-like service instead of storing state in global extension variables, which could lead to issues with concurrent operations or extension state management.

Suggested change
/**
* Clipboard-like service for managing copied collection nodes.
*/
export class CollectionClipboardService {
private static _copiedCollectionNode: CollectionItem | undefined;
public static setCopiedCollectionNode(node: CollectionItem | undefined): void {
CollectionClipboardService._copiedCollectionNode = node;
}
public static getCopiedCollectionNode(): CollectionItem | undefined {
return CollectionClipboardService._copiedCollectionNode;
}
public static clearCopiedCollectionNode(): void {
CollectionClipboardService._copiedCollectionNode = undefined;
}
}

Copilot uses AI. Check for mistakes.

// Check type of sourceNode or targetNodeAdd commentMore actions
// Currently we only support CollectionItem types
// Later we need to check if they are supported types that with document reader and writer implementations

Copilot AI Aug 12, 2025

Copy link

Choose a reason for hiding this comment

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

The comment contains a formatting error: 'targetNodeAdd commentMore actions' should be properly formatted and clarified.

Suggested change
// Later we need to check if they are supported types that with document reader and writer implementations
// Check type of sourceNode and targetNode
// Currently we only support CollectionItem types
// Later we need to check if they are supported types that have document reader and writer implementations

Copilot uses AI. Check for mistakes.
);

// void vscode.window.showInformationMessage(`${sourceInfo}\n${targetInfo}`);
// Confirm the copy operation with the userAdd commentMore actions

Copilot AI Aug 12, 2025

Copy link

Choose a reason for hiding this comment

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

The comment contains a formatting error: 'userAdd commentMore actions' should be properly formatted and clarified.

Suggested change
// Confirm the copy operation with the userAdd commentMore actions
// Confirm the copy operation with the user

Copilot uses AI. Check for mistakes.
Comment thread src/commands/pasteCollection/pasteCollection.ts
// config.source.databaseName,
// config.source.collectionName,
// );

Copilot AI Aug 12, 2025

Copy link

Choose a reason for hiding this comment

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

This commented-out code block should be removed if it's not needed, as it adds unnecessary clutter to the codebase.

Suggested change

Copilot uses AI. Check for mistakes.
if (result.errors !== null) {
// For basic implementation with abort strategy, any error should fail the task
if (this.config.onConflict === ConflictResolutionStrategy.Abort) {
const firstError = result.errors[0] as { error: Error };

Copilot AI Aug 12, 2025

Copy link

Choose a reason for hiding this comment

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

Using type assertion with 'as' violates the TypeScript strict mode guidelines. The code should use proper type guards or handle the possibility that errors[0] might not exist or have the expected structure.

Copilot uses AI. Check for mistakes.
const session = client.startTransaction();
const collection = client.getCollection(databaseName, collectionName);
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return

Copilot AI Aug 12, 2025

Copy link

Choose a reason for hiding this comment

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

The eslint-disable comment indicates unsafe type handling. The getOperation() method should be properly typed instead of disabling type safety warnings.

Copilot uses AI. Check for mistakes.
Comment thread src/documentdb/ClustersClient.ts Outdated

@tnaum-ms tnaum-ms left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@xingfan-git I added my review comments and now I'll move forward with changes, as discussed.

This means that my comments might be out of date, but please read these anyway to receive feedback.

We'll discuss next step in a dedicated PR.

collectionName: string,
documents: Document[],
): Promise<InsertDocumentsResult> {
ordered: boolean = true,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

we should improve documentation here

Comment thread src/documentdb/DocumentProvider.ts Outdated
): AsyncIterable<DocumentDetails> {
const client = await ClustersClient.getClient(connectionId);

const documentStream = client.streamDocuments(databaseName, collectionName, new AbortController().signal);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

let's make sure we can abort this.

Comment on lines +288 to 315
try {
const insertResult = await client.insertDocuments(
databaseName,
collectionName,
documentsToProcess as Document[],
false,
);
return {
count: insertResult.insertedCount,
errorOccurred: false,
};
} catch (error) {
if (isMongoBulkWriteError(error)) {
// Handle MongoDB bulk write errors
// It could be a partial failure, so we need to check the result
return {
count: error.result.insertedCount,
errorOccurred: true,
};
} else {
// Handle other errors
return {
count: 0,
errorOccurred: true,
};
}
}
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@xingfan-git thanks for this update. Let's keep features/fixes separate to simplify feature delivery. This could be in a separate issue/branch/PR

Comment thread src/commands/pasteCollection/pasteCollection.ts
Comment on lines +102 to +137

// Wait for the task to complete
while (task.getStatus().state === TaskState.Running || task.getStatus().state === TaskState.Initializing) {
// Simple polling with a small delay
await new Promise((resolve) => setTimeout(resolve, 100));
}

// Check final status and show result
const finalStatus = task.getStatus();
if (finalStatus.state === TaskState.Completed) {
void vscode.window.showInformationMessage(
l10n.t('Collection copied successfully: {0}', finalStatus.message || ''),
);
} else if (finalStatus.state === TaskState.Failed) {
const errorToThrow =
finalStatus.error instanceof Error ? finalStatus.error : new Error('Copy operation failed');
throw errorToThrow;
} else if (finalStatus.state === TaskState.Stopped) {
void vscode.window.showInformationMessage(l10n.t('Copy operation was cancelled.'));
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
void vscode.window.showErrorMessage(l10n.t('Failed to copy collection: {0}', errorMessage));
throw error;
} finally {
// Clean up - remove the task from the service after completion
try {
const task = TaskService.listTasks().find((t) => t.type === 'copy-paste-collection');
if (task) {
await TaskService.deleteTask(task.id);
}
} catch (cleanupError) {
// Log cleanup error but don't throw
console.warn('Failed to clean up copy-paste task:', cleanupError);
}
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@xingfan-git we should not wait for the task to complete here - it's a fire and forget scenario. If the task needs work to be done once it finishes, it needs to be done in the task itself. if we need extra "post task" UI step etc., and you think that the architecture does not support it, then it's something we should consier in the architecture and extend the Task API.

I'll remove/comment it out for discussion.

return documents;
}

async countDocuments(databaseName: string, collectionName: string, findQuery: string = '{}'): Promise<number> {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

TODO: create an issue to add support for "abort" support to runQuery and for countDocuments

Comment on lines +136 to +141
if (signal.aborted) {
// Cleanup any remaining buffer
if (buffer.length > 0) {
await this.flushBuffer(buffer);
}
return;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

hm, let's just abort without writing more into the target collection.

Comment on lines +65 to +77
export interface DocumentDetails {
/**
* The document's unique identifier (e.g., _id in MongoDB)
*/
id: unknown;

/**
* The document content treated as opaque data by the core task logic.
* Specific readers/writers will know how to interpret/serialize this.
* For MongoDB, this would typically be a BSON document.
*/
documentContent: unknown;
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@xingfan-git let's discuss adding a size estimate here so maybe we can get this from the document reader and keep it so that it's always available.

@tnaum-ms tnaum-ms left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

submitting comments

const count = await collection.countDocuments(findQueryObj, {
// Use a read preference of 'primary' to ensure we get the most up-to-date
// count, especially important for sharded clusters.
readPreference: 'primary',

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@xingfan-git good catch!

@tnaum-ms tnaum-ms merged commit 35221cb into feature/copy-and-paste Aug 13, 2025
5 checks passed
@tnaum-ms tnaum-ms deleted the dev/xingfan/111-copy-and-paste-2-implement-basic-copy-and-paste-task branch August 13, 2025 13:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Copy-and-Paste: 2. Implement Basic Copy-and-Paste Task

3 participants