Skip to content

Commit

Permalink
report handwritten code events to Faros
Browse files Browse the repository at this point in the history
  • Loading branch information
meldiner committed Jan 27, 2025
1 parent 91ccb28 commit 723da8a
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 89 deletions.
60 changes: 29 additions & 31 deletions activepieces-flow.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "faros-vscode-extension-webhook",
"description": "This workflow handles webhook events from the VSCode extension, authenticates with Faros, and sends the data to the Faros API.",
"description": "",
"tags": [],
"pieces": [
"@activepieces/piece-webhook",
"@activepieces/piece-http"
"@activepieces/piece-faros-advanced"
],
"template": {
"displayName": "Faros VSCode Extension Webhook",
Expand All @@ -15,19 +15,20 @@
"type": "PIECE_TRIGGER",
"settings": {
"pieceName": "@activepieces/piece-webhook",
"pieceVersion": "0.1.5",
"pieceVersion": "0.1.10",
"pieceType": "OFFICIAL",
"packageType": "REGISTRY",
"input": {
"authType": "none",
"authFields": {}
},
"inputUiInfo": {},
"inputUiInfo": {
"customizedInputs": {}
},
"triggerName": "catch_webhook"
},
"nextAction": {
"name": "step_1",
"displayName": "Ensure permitted mutations",
"name": "step_2",
"type": "CODE",
"valid": true,
"settings": {
Expand All @@ -38,7 +39,9 @@
"code": "export const code = async (inputs) => {\n const input = inputs.body.query;\n \n // Define the list of permitted mutations\n const permittedMutations = [\n 'insert_vcs_User_one',\n 'insert_vcs_UserTool_one',\n 'insert_vcs_UserToolUsage_one',\n 'update_columns',\n 'insert_vcs_File_one',\n 'insert_vcs_Branch_one',\n 'insert_vcs_Repository_one'\n ];\n\n // Regular expression to match any mutation starting with insert_, update_, or delete_\n const mutationRegex = /\\b(insert_|update_|delete_)\\w+/g;\n\n // Find all mutations in the input string\n const mutations = input.match(mutationRegex);\n\n // If no mutations are found, return true (no unpermitted mutation)\n if (!mutations) {\n return true; // No mutation found, hence no unpermitted mutation\n }\n\n // Check if all mutations found are in the permitted list\n const hasUnpermittedMutation = mutations.some(mutation => !permittedMutations.includes(mutation));\n\n if (hasUnpermittedMutation) {\n throw new Error(\"Unpermitted mutation identified\");\n }\n\n return true;\n};",
"packageJson": "{}"
},
"inputUiInfo": {},
"inputUiInfo": {
"customizedInputs": {}
},
"errorHandlingOptions": {
"retryOnFailure": {
"value": false
Expand All @@ -49,32 +52,24 @@
}
},
"nextAction": {
"name": "step_2",
"displayName": "Update graph",
"name": "step_3",
"skip": false,
"type": "PIECE",
"valid": true,
"settings": {
"input": {
"url": "https://prod.api.faros.ai/graphs/default/graphql",
"body": {
"data": "{{trigger['body']}}"
},
"method": "POST",
"headers": {
"authorization": "<KEY>"
},
"failsafe": false,
"body_type": "json",
"use_proxy": false,
"queryParams": {},
"proxy_settings": {}
"graph": "default",
"query": "{{trigger['body']['query']}}",
"variables": {}
},
"pieceName": "@activepieces/piece-http",
"pieceType": "OFFICIAL",
"actionName": "send_request",
"inputUiInfo": {},
"packageType": "REGISTRY",
"pieceVersion": "0.5.1",
"pieceName": "@activepieces/piece-faros-advanced",
"pieceType": "CUSTOM",
"actionName": "writeMutations",
"inputUiInfo": {
"customizedInputs": {}
},
"packageType": "ARCHIVE",
"pieceVersion": "0.0.2",
"errorHandlingOptions": {
"retryOnFailure": {
"value": false
Expand All @@ -83,11 +78,14 @@
"value": false
}
}
}
}
},
"displayName": "Write Mutations"
},
"displayName": "Ensure permitted mutations"
}
},
"valid": true
"valid": true,
"schemaVersion": "1"
},
"blogUrl": ""
}
33 changes: 26 additions & 7 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from "vscode";
import { send } from "./sender";
import { send, squash } from "./sender";
import { farosConfig, updateConfig } from "./config";
import { addAutoCompletionEvent, addHandWrittenEvent, clearAutoCompletionEventQueue, getAutoCompletionEventQueue, setContext } from "./state";
import { addAutoCompletionEvent, addHandWrittenEvent, clearAutoCompletionEventQueue, clearHandWrittenEventQueue, getAutoCompletionEventQueue, getHandWrittenEventQueue, setContext } from "./state";
import { getGitBranch, getGitRepoName } from "./git";
import * as path from "path";
import FarosPanel from "./panel";
Expand All @@ -18,12 +18,29 @@ let farosPanel: FarosPanel | null = null;

// Function to check and log events every minute
function checkAndLogEvents() {
if (getAutoCompletionEventQueue().length > 0) {
console.log("Sending autocompletion events:", getAutoCompletionEventQueue());
send(getAutoCompletionEventQueue());
const autoCompletionEvents = getAutoCompletionEventQueue();
if (autoCompletionEvents.length > 0) {
console.log("Sending autocompletion events:", autoCompletionEvents);
try {
send(autoCompletionEvents, 'AutoCompletion');
} catch (error) {
console.error("Error sending autocompletion events:", error);
}
// Clear the events after logging
clearAutoCompletionEventQueue();
}

const handWrittenEvents = getHandWrittenEventQueue();
if (handWrittenEvents.length > 0) {
console.log("Sending hand written events:", handWrittenEvents);
try {
send(squash(handWrittenEvents), 'HandWritten');
} catch (error) {
console.error("Error sending hand written events:", error);
}
// Clear the events after logging
clearHandWrittenEventQueue();
}
}

// Set interval to check and log events every minute (60000 milliseconds)
Expand Down Expand Up @@ -98,7 +115,8 @@ function registerSuggestionListener() {
// Store the event in memory
addAutoCompletionEvent({
timestamp: new Date(),
autoCompletionCharCountChange: currentLengthChange,
charCountChange: currentLengthChange,
type: 'AutoCompletion',
filename: activeEditor.document.fileName,
extension: path.extname(activeEditor.document.fileName),
language: activeEditor.document.languageId,
Expand All @@ -110,7 +128,8 @@ function registerSuggestionListener() {
} else if (changeType === TextChangeType.HandWrittenChar) {
addHandWrittenEvent({
timestamp: new Date(),
handWrittenCharCountChange: 1,
charCountChange: 1,
type: 'HandWritten',
filename: activeEditor.document.fileName,
extension: path.extname(activeEditor.document.fileName),
language: activeEditor.document.languageId,
Expand Down
140 changes: 140 additions & 0 deletions src/sender.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import * as assert from 'assert';
import { squash } from './sender';
import { DocumentChangeEvent } from './types';

suite('squash', () => {
test('should combine events in the same minute for the same file', () => {
const baseTime = new Date('2024-01-01T10:30:45.123Z');
const events: DocumentChangeEvent[] = [
{
timestamp: baseTime,
filename: 'test.ts',
charCountChange: 10,
repository: 'repo',
branch: 'main',
type: 'HandWritten'
},
{
timestamp: new Date('2024-01-01T10:30:55.456Z'), // Same minute
filename: 'test.ts',
charCountChange: 5,
repository: 'repo',
branch: 'main',
type: 'HandWritten'
},
];

const result = squash(events);
assert.strictEqual(result.length, 1);
assert.deepStrictEqual(result[0], {
timestamp: baseTime,
filename: 'test.ts',
charCountChange: 15, // 10 + 5
repository: 'repo',
branch: 'main',
type: 'HandWritten'
});
});

test('should keep events in different minutes separate', () => {
const events: DocumentChangeEvent[] = [
{
timestamp: new Date('2024-01-01T10:30:45.123Z'),
filename: 'test.ts',
charCountChange: 10,
type: 'HandWritten'
},
{
timestamp: new Date('2024-01-01T10:31:00.000Z'), // Different minute
filename: 'test.ts',
charCountChange: 5,
type: 'HandWritten'
},
];

const result = squash(events);
assert.strictEqual(result.length, 2);
assert.strictEqual(result[0].charCountChange, 10);
assert.strictEqual(result[1].charCountChange, 5);
});

test('should keep events for different files separate within same minute', () => {
const baseTime = new Date('2024-01-01T10:30:45.123Z');
const events: DocumentChangeEvent[] = [
{
timestamp: baseTime,
filename: 'test1.ts',
charCountChange: 10,
type: 'HandWritten'
},
{
timestamp: baseTime,
filename: 'test2.ts',
charCountChange: 5,
type: 'HandWritten'
},
];

const result = squash(events);
assert.strictEqual(result.length, 2);
assert.strictEqual(result.find(e => e.filename === 'test1.ts')?.charCountChange, 10);
assert.strictEqual(result.find(e => e.filename === 'test2.ts')?.charCountChange, 5);
});

test('should handle events with no filename', () => {
const baseTime = new Date('2024-01-01T10:30:45.123Z');
const events: DocumentChangeEvent[] = [
{
timestamp: baseTime,
charCountChange: 10,
type: 'HandWritten'
},
{
timestamp: baseTime,
charCountChange: 5,
type: 'HandWritten'
},
];

const result = squash(events);
assert.strictEqual(result.length, 1);
assert.strictEqual(result[0].charCountChange, 15);
assert.strictEqual(result[0].filename, undefined);
});

test('should preserve other properties from first event in group', () => {
const baseTime = new Date('2024-01-01T10:30:45.123Z');
const events: DocumentChangeEvent[] = [
{
timestamp: baseTime,
filename: 'test.ts',
charCountChange: 10,
repository: 'repo1',
branch: 'main',
language: 'typescript',
type: 'HandWritten'
},
{
timestamp: baseTime,
filename: 'test.ts',
charCountChange: 5,
repository: 'repo2',
branch: 'feature',
language: 'typescript',
type: 'HandWritten'
},
];

const result = squash(events);
assert.strictEqual(result.length, 1);
assert.deepStrictEqual(result[0], {
timestamp: baseTime,
filename: 'test.ts',
charCountChange: 15,
repository: 'repo1',
branch: 'main',
language: 'typescript',
type: 'HandWritten'
});
});
});
Loading

0 comments on commit 723da8a

Please sign in to comment.