Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tools #8

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
109 changes: 109 additions & 0 deletions force-app/main/tools/classes/zChatterAgentTool.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
public class zChatterAgentTool implements IAgentTool {
public String getDescription() {
return 'Chatter Agent Tool for retrieving, inserting, updating, upserting, and deleting Chatter posts';
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a lot of context for a single tool. I think the agent will react much better to have all of these split out into separate tools (RetrieveChatterPost, CreateChatterPost, DeleteChatterPost).

The way it current is, the agent will likely get confused how to use it.

}

public Map<String, String> getParameters() {
return new Map<String, String>{
'operation' => 'The action to be performed: insert, delete, retrieve, update, or upsert on the Chatter post',
'Id' => 'The unique identifier of the Chatter post to be retrieved, updated, or deleted',
'parentId' => 'The unique identifier of the parent record, such as the Account record Id for a post on the Account record page',
'title' => 'The title of the feed item; for LinkPost type, this is the link name; can only be updated on posts of type QuestionPost',
'body' => 'The main text content of the Chatter post',
'isRichText' => 'Indicates whether the post body contains rich text formatting: true or false',
'linkUrl' => 'The web address for LinkPost type.',
'visibility' => 'Optional. AllUsers, InternalUsers. InternalUsers is the default.',
'type' => 'Optional. Default is TextPost. LinkPost, QuestionPost, or TextPost.',
'lastEditById' => 'The unique identifier of the Salesforce User who made the most recent edit. Optional',
'lastEditDate' => 'The date and time of the most recent edit, as a DateTime string. Optional.',
'revision' => 'The version number of the Chatter post as an Integer. Optional.',
'status' => 'The current state of the Chatter post: Published or PendingReview. The value Published means publicly available while PendingReview means awaiting approval.'
};
}

public String execute(Map<String, String> args) {
String operation = args.get('operation');
if (String.isEmpty(operation)) {
throw new Agent.ActionRuntimeException('Missing required parameter: operation');
}
operation = operation.toLowerCase();
switch on operation {
when 'delete' {
return deletePost(args.get('Id'));
}
when 'retrieve' {
return retrievePost(args.get('Id'));
}
when 'insert','update','upsert' {
return insertUpdateUpsert(args);
}
when else {
throw new Agent.ActionRuntimeException('Invalid operation: ' + operation);
}
}
}

private FeedItem setFeedItemFields(FeedItem sobj, Map<String, String> args) {
for (String key : args.keySet()) {
if(key=='operation') {
continue;
}
if (args.get(key) != null) {
if (key == 'isRichText') {
sobj.put(key, Boolean.valueOf(args.get(key)));
} else if (key == 'lastEditDate') {
sobj.put(key, DateTime.valueOf(args.get(key)));
} else if (key == 'revision') {
sobj.put(key, Integer.valueOf(args.get(key)));
} else {
sobj.put(key, args.get(key));
}
}
}
return sobj;
}

private String retrievePost(String Id) {
FeedItem post = [SELECT Id, parentId, Body, isRichText, linkUrl, visibility, type, CreatedDate, LastEditById, LastEditDate, Revision, Status FROM FeedItem WHERE Id = :Id];
return JSON.serialize(post);
}

private String deletePost(String Id) {
FeedItem post = [SELECT Id FROM FeedItem WHERE Id = :Id];
delete post;
return 'Post deleted with Id: ' + id;
}

private String insertUpdateUpsert(Map<String, String> args) {
String retval = '';
String operation = args.get('operation');
if(operation == 'insert') {
if (String.isEmpty(args.get('parentId'))) {
throw new Agent.ActionRuntimeException('Missing required parameter: parentId');
}
if (String.isEmpty(args.get('body'))) {
throw new Agent.ActionRuntimeException('Missing required parameter: body');
}
} else if (operation == 'update' || operation == 'upsert') {
if (String.isEmpty(args.get('Id'))) {
throw new Agent.ActionRuntimeException('Missing required parameter: Id');
}
}
FeedItem post = new FeedItem();
if (operation == 'update' || operation == 'upsert') {
post.Id = args.get('Id');
}
post = setFeedItemFields(post, args);
if(operation == 'insert') {
insert post;
retval = 'Post inserted with Id: ' + post.Id;
} else if(operation == 'update') {
update post;
retval = 'Post updated with Id: ' + post.Id;
} else if (operation == 'upsert') {
upsert post;
retval = 'Post upserted with Id: ' + post.Id;
}
return retval;
}
}
5 changes: 5 additions & 0 deletions force-app/main/tools/classes/zChatterAgentTool.cls-meta.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>56.0</apiVersion>
<status>Active</status>
</ApexClass>
109 changes: 109 additions & 0 deletions force-app/main/tools/classes/zChatterAgentToolTest.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
@isTest
public class zChatterAgentToolTest {

@testSetup
static void setup() {
// Create a test Account
Account testAccount = new Account(Name = 'Test Chatter Account');
insert testAccount;

// Create a test FeedItem
FeedItem testFeedItem = new FeedItem(parentId = testAccount.Id, Body = 'Test post', Type = 'TextPost');
insert testFeedItem;
}

@isTest
static void testGetDescription() {
zChatterAgentTool agentTool = new zChatterAgentTool();
String description = agentTool.getDescription();
System.assertNotEquals(null, description);
}

@isTest
static void testGetParameters() {
zChatterAgentTool agentTool = new zChatterAgentTool();
Map<String, String> parameters = agentTool.getParameters();
System.assertNotEquals(null, parameters);
}

@isTest
static void testExecuteInsert() {
Account testAccount = [SELECT Id FROM Account WHERE Name = 'Test Chatter Account'];
FeedItem testPost = [SELECT Id FROM FeedItem WHERE ParentId =: testAccount.Id];

// Test insert operation
Map<String, String> args = new Map<String, String>{
'operation' => 'insert',
'parentId' => testAccount.Id,
'body' => 'Test insert post',
'type' => 'TextPost'
};
zChatterAgentTool agentTool = new zChatterAgentTool();
String result = agentTool.execute(args);
System.assert(result.contains('Post inserted with Id:'));
}

@isTest
static void testExecuteRetrieve() {
Account testAccount = [SELECT Id FROM Account WHERE Name = 'Test Chatter Account'];
FeedItem testPost = [SELECT Id FROM FeedItem WHERE ParentId =: testAccount.Id];

// Test retrieve operation
Map<String, String> args = new Map<String, String>{
'operation' => 'retrieve',
'Id' => testPost.Id
};
zChatterAgentTool agentTool = new zChatterAgentTool();
String result = agentTool.execute(args);
System.assertNotEquals(null, result);
}

//@isTest
static void testExecuteUpdate() {
Account testAccount = [SELECT Id FROM Account WHERE Name = 'Test Chatter Account'];
FeedItem testPost = [SELECT Id FROM FeedItem WHERE ParentId =: testAccount.Id];

// Test update operation
Map<String, String> args = new Map<String, String>{
'operation' => 'update',
'Id' => testPost.Id,
'body' => 'Test update post'
};
zChatterAgentTool agentTool = new zChatterAgentTool();
String result = agentTool.execute(args);
System.assert(result.contains('Post updated with Id:'));
}

//@isTest
static void testExecuteUpsert() {
Account testAccount = [SELECT Id FROM Account WHERE Name = 'Test Chatter Account'];
FeedItem testPost = [SELECT Id FROM FeedItem WHERE ParentId =: testAccount.Id];

// Test upsert operation
Map<String, String> args = new Map<String, String>{
'operation' => 'upsert',
'Id' => testPost.Id,
'parentId' => testAccount.Id,
'body' => 'Test upsert post',
'type' => 'TextPost'
};
zChatterAgentTool agentTool = new zChatterAgentTool();
String result = agentTool.execute(args);
System.assert(result.contains('Post upserted with Id:'));
}

//@isTest
static void testExecuteDelete() {
Account testAccount = [SELECT Id FROM Account WHERE Name = 'Test Chatter Account'];
FeedItem testPost = [SELECT Id FROM FeedItem WHERE ParentId =: testAccount.Id];

// Test delete operation
Map<String, String> args = new Map<String, String>{
'operation' => 'delete',
'Id' => testPost.Id
};
zChatterAgentTool agentTool = new zChatterAgentTool();
String result = agentTool.execute(args);
System.assert(result.contains('Post deleted with Id:'));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>56.0</apiVersion>
<status>Active</status>
</ApexClass>
50 changes: 50 additions & 0 deletions force-app/main/tools/classes/zFileCreationAgentTool.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
public class zFileCreationAgentTool implements IAgentTool {
public String getDescription() {
return 'Create Salesforce files and optionally link them to Salesforce records';
}

public Map<String, String> getParameters() {
return new Map<String, String>{
'fileName' => 'The name of the file to be created',
'fileTextContent' => 'File content as a string',
'recordId' => 'The Salesforce record Id to which the file should be linked (optional)'
};
}

private Blob base64Encode(String data) {
Blob inputBlob = Blob.valueOf(data);
return Blob.valueOf(EncodingUtil.base64Encode(inputBlob));
}

public String execute(Map<String, String> args) {
if (!args.containsKey('fileName')) {
throw new Agent.ActionRuntimeException('Missing required parameter: fileName');
}

if (!args.containsKey('fileTextContent')) {
throw new Agent.ActionRuntimeException('Missing required parameter: fileTextContent');
}

String fileName = args.get('fileName');
String fileContent = args.get('fileTextContent');

ContentVersion cv = new ContentVersion();
cv.Title = fileName;
cv.PathOnClient = fileName;
cv.VersionData = base64Encode(fileContent);
cv.IsMajorVersion = true;
insert cv;

if (args.containsKey('recordId')) {
ContentVersion cv2 = [SELECT ContentDocumentId FROM ContentVersion WHERE Id =: cv.Id];

ContentDocumentLink cdl = new ContentDocumentLink();
cdl.LinkedEntityId = args.get('recordId');
cdl.ContentDocumentId = cv2.ContentDocumentId;
cdl.shareType = 'V';
insert cdl;
}

return 'File created with Id: ' + cv.id;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>56.0</apiVersion>
<status>Active</status>
</ApexClass>
94 changes: 94 additions & 0 deletions force-app/main/tools/classes/zFileCreationAgentToolTest.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
@isTest
public class zFileCreationAgentToolTest {

@isTest
private static void test_getDescription() {
zFileCreationAgentTool tool = new zFileCreationAgentTool();
String description = tool.getDescription();
System.assertEquals('Create Salesforce files and optionally link them to Salesforce records', description);
}

@isTest
private static void test_getParameters() {
zFileCreationAgentTool tool = new zFileCreationAgentTool();
Map<String, String> parameters = tool.getParameters();
System.assertEquals(3, parameters.size());
System.assertEquals('The name of the file to be created', parameters.get('fileName'));
System.assertEquals('File content as a string', parameters.get('fileTextContent'));
System.assertEquals('The Salesforce record Id to which the file should be linked (optional)', parameters.get('recordId'));
}

@isTest
private static void test_execute_missingFileName() {
zFileCreationAgentTool tool = new zFileCreationAgentTool();
Map<String, String> args = new Map<String, String> {
'fileTextContent' => 'Test content'
};

Test.startTest();
Exception e = null;
try {
tool.execute(args);
} catch (Exception ex) {
e = ex;
}
Test.stopTest();

System.assertNotEquals(null, e);
System.assertEquals('Missing required parameter: fileName', e.getMessage());
}

@isTest
private static void test_execute_missingFileContent() {
zFileCreationAgentTool tool = new zFileCreationAgentTool();
Map<String, String> args = new Map<String, String> {
'fileName' => 'Test file'
};

Test.startTest();
Exception e = null;
try {
tool.execute(args);
} catch (Exception ex) {
e = ex;
}
Test.stopTest();

System.assertNotEquals(null, e);
System.assertEquals('Missing required parameter: fileTextContent', e.getMessage());
}

@isTest
private static void test_execute_withoutRecordId() {
zFileCreationAgentTool tool = new zFileCreationAgentTool();
Map<String, String> args = new Map<String, String> {
'fileName' => 'Test file',
'fileTextContent' => 'Test content'
};

Test.startTest();
String result = tool.execute(args);
Test.stopTest();

System.assert(result.startsWith('File created with Id: '));
}

@isTest
private static void test_execute_withRecordId() {
Account testAccount = new Account(Name='Test Account');
insert testAccount;

zFileCreationAgentTool tool = new zFileCreationAgentTool();
Map<String, String> args = new Map<String, String> {
'fileName' => 'Test file',
'fileTextContent' => 'Test content',
'recordId' => testAccount.Id
};

Test.startTest();
String result = tool.execute(args);
Test.stopTest();

System.assert(result.startsWith('File created with Id: '));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>56.0</apiVersion>
<status>Active</status>
</ApexClass>
Loading