

Last date modified: September 24 2025
Custom agents perform background processing within your application without interfering with user activities performed through the Relativity UI. You can implement agents as managers that monitor tasks or workers that perform specific jobs in your environment.
In this lesson, you will learn how to complete these tasks:
Estimated completion time - 1 hour
This lesson describes all the steps necessary to build the agent but you can also download the completed code from WikipediaAgent.zip.
A cron job is the process of scheduling tasks to be executed at a future time. The agent framework in Relativity follows a similar approach when handling long running tasks.
Use the following steps to create an agent from a template:
1
2
3
4
5
namespace WikipediaAgent
{
[kCura.Agent.CustomAttributes.Name("Wikipedia Agent")]
[System.Runtime.InteropServices.Guid("44ae8827-1c7f-422c-849d-197c1c0f5b68")]
public class WikipediaAgent : AgentBase
1
2
3
4
5
6
7
public override string Name
{
get
{
return "Wikipedia Agent-";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using kCura.Agent;
using Relativity.API;
using Relativity.ObjectManager.V1.Interfaces;
using System;
using System.Net;
namespace WikipediaAgent {
[kCura.Agent.CustomAttributes.Name("Wikipedia Agent")]
[System.Runtime.InteropServices.Guid("7d1c6f96-f59b-4c96-a749-aaf0493b7e61")]
public class WikipediaAgent: AgentBase {
/// <summary>
/// Agent logic goes here
/// </summary>
public override void Execute() {
IAPILog logger = Helper.GetLoggerFactory().GetLogger();
try {
// Update Security Protocol
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//Get the current Agent artifactID
int agentArtifactId = AgentID;
//Get a dbContext for the EDDS database
IDBContext eddsDbContext = Helper.GetDBContext(-1);
//Get a dbContext for the workspace database
//int workspaceArtifactId = 01010101; // Update it with the real
//IDBContext workspaceDbContext = Helper.GetDBContext(workspaceArtifactId);
//Get GUID for an artifact
//int testArtifactId = 10101010;
//Guid guidForTestArtifactId = Helper.GetGuid(workspaceArtifactId, testArtifactId);
//Display a message in Agents Tab and Windows Event Viewer
RaiseMessage("Hello World The current time is: " + DateTime.Now.ToLongTimeString(), 1);
// To interact with Relativity, refer to the APIs documented on platform.relativity.com.
// The Object Manager is one of the most popular APIs.
// Relativity Services API (RSAPI) is obsolete and will not work.
using (IObjectManager objectManager = this.Helper.GetServicesManager().CreateProxy < IObjectManager > (ExecutionIdentity.CurrentUser)) {
}
logger.LogVerbose("Log information throughout execution.");
} catch (Exception ex) {
//Your Agent caught an exception
logger.LogError(ex, "There was an exception.");
RaiseError(ex.Message, ex.Message);
}
}
/// <summary>
/// Returns the name of agent
/// </summary>
public override string Name {
get {
return "Wikipedia Agent";
}
}
}
}
Use the following steps to upload the agent to a Relativity application:
After associating your agent with an application, you can add this agent to your Relativity instance.
Use the following steps to add your agent:
After deploying your agent in a Relativity instance, you can start remotely debugging it (as long as you have also uploaded the debug symbols file as a resource for your application). The following steps are similar to those for debugging a Kepler service. See Lesson 3 - Create a RESTful API.
Make sure that you have the Visual Studio Remote Debugger service running on the Relativity instance where you want to debug.
Use the following steps to remotely debug an agent:
After creating the IWikipediaArticleManager and implementing all the methods on it, your agent can perform these tasks:
Use the following steps to update the functionality of your agent:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WikipediaAgent {
public static class Constants {
public
const int NUMBER_OF_ARTICLES = 10;
public
const int NUMBER_OF_ENTRIES_TO_QUERY = 100;
public
const int DOCUMENT_ARTIFACT_ID = 10;
public
const int CONTROL_NUMBER_FIELD_ID = 1003667;
#if DEBUG
// Change this constant to your local instance when using the Relativity Local Debugger, exemplfied by https://RelativityDevVm-12.2.190.4/RelativityWebAPI.
public
const string WEB_SERVICE_URL = "http://localhost/RelativityWebAPI";
#else
public
const string WEB_SERVICE_URL = "http://localhost/RelativityWebAPI";
#endif
public static readonly Guid HELLO_WIKIPEDIA_APP_GUID = new Guid("E57FA0FE-59FD-49EB-92ED-895F3E592CD1");
public static readonly Guid CATEGORY_OBJECT_TYPE_GUID = new Guid("6b20f149-1b17-4e9c-8403-439e98e8bfd2");
public static readonly Guid REFERENCE_OBJECT_TYPE_GUID = new Guid("f5e7f198-9d2e-445f-a42c-9e248ea85c51");
public static readonly Guid CATEGORY_NAME_FIELD_GUID = new Guid("16D8A362-2923-45B7-8444-7339C57B3AF0");
public static readonly Guid CATEGORY_OVERWRITE_FIELD_GUID = new Guid("042e0329-1467-4993-8188-66615e103de3");
public static readonly Guid CATEGORY_UPDATES_FIELD_GUID = new Guid("f365de2e-a641-428f-9188-a3970a7c308f");
public static readonly Guid REFERENCE_NAME_FIELD_GUID = new Guid("bb4b7fca-7b9a-4ae0-8ac0-7684a1b34d3b");
public static readonly Guid REFERENCE_URL_FIELD_GUID = new Guid("086D0E22-72B2-42F4-B0F4-A377A5DD6890");
public static readonly Guid REFERENCE_ARTICLE_CATEGORIES_FIELD_GUID = new Guid("2F9E6B70-B17D-4A1D-8987-EBB70239FD20");
}
}
Add a reference to the WikipediaKepler.Interfaces project:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
namespace WikipediaAgent {
public class Article {
private
const int _HASH_MULTIPLIER_INT = 397;
public string Category {
get;
set;
}
public string Title {
get;
set;
}
public string Url {
get;
set;
}
public bool OverwriteExisting {
get;
set;
}
protected bool Equals(Article other) {
return Category == other.Category && Title == other.Title && Url == other.Url;
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != this.GetType()) {
return false;
}
return Equals((Article) obj);
}
public override int GetHashCode() {
unchecked {
int hashCode = (Category != null ? Category.GetHashCode() : 0);
hashCode = (hashCode * _HASH_MULTIPLIER_INT) ^ (Title != null ? Title.GetHashCode() : 0);
hashCode = (hashCode * _HASH_MULTIPLIER_INT) ^ (Url != null ? Url.GetHashCode() : 0);
return hashCode;
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
namespace WikipediaAgent {
public class ArticleCategory {
private
const int _HASH_MULTIPLIER_INT = 397;
public string Name {
get;
set;
}
public bool OverwriteArticleText {
get;
set;
}
protected bool Equals(ArticleCategory other) {
return Name == other.Name && OverwriteArticleText == other.OverwriteArticleText;
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != this.GetType()) {
return false;
}
return Equals((ArticleCategory) obj);
}
public override int GetHashCode() {
unchecked {
return ((Name != null ? Name.GetHashCode() : 0) * _HASH_MULTIPLIER_INT) ^ OverwriteArticleText.GetHashCode();
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Threading.Tasks;
using kCura.Relativity.DataReaderClient;
namespace WikipediaAgent {
public interface IWikipediaArticleManager {
Task<IEnumerable<int>> GetHelloWikipediaWorkspaceIDs();
Task<IEnumerable<ArticleCategory>> GetArticleCategories(int workspaceID);
Task<IEnumerable<Article>> GetArticles(IEnumerable<ArticleCategory> articleCategories, int numArticlesPerCategory);
Task<ImportBulkArtifactJob> BuildArticleImportJob(int workspaceID, IEnumerable<Article> articles, TempFileCollection tempFileCollection);
Task AddOrUpdateArticleReferences(int workspaceID, IEnumerable<Article> articles);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using kCura.Relativity.DataReaderClient;
using kCura.Relativity.ImportAPI;
using Relativity.API;
using Relativity.ObjectManager.V1.Interfaces;
using Relativity.ObjectManager.V1.Models;
using Relativity.Services.Interfaces.LibraryApplication;
using Relativity.Services.Interfaces.LibraryApplication.Models;
using Relativity.Services.Interfaces.LibraryApplication.Models.Query;
using WikipediaKepler.Interfaces.WikipediaManagement.v1;
using WikipediaKepler.Interfaces.WikipediaManagement.v1.Models;
using Sort = Relativity.Services.Interfaces.LibraryApplication.Models.Query.Sort;
namespace WikipediaAgent {
public class WikipediaArticleManager: IWikipediaArticleManager {
private readonly IApplicationInstallManager _applicationInstallManager;
private readonly IObjectManager _objectManager;
private readonly IWikipediaService _wikipediaService;
private readonly IImportAPI _importApi;
private readonly IAPILog _logger;
public WikipediaArticleManager(IApplicationInstallManager applicationInstallManager, IObjectManager objectManager, IWikipediaService wikipediaService, IImportAPI importApi, IAPILog logger) {
_applicationInstallManager = applicationInstallManager;
_objectManager = objectManager;
_wikipediaService = wikipediaService;
_importApi = importApi;
_logger = logger;
}
public async Task < IEnumerable < int >> GetHelloWikipediaWorkspaceIDs() {
throw new NotImplementedException();
}
public async Task < IEnumerable < ArticleCategory >> GetArticleCategories(int workspaceID) {
throw new NotImplementedException();
}
public async Task < IEnumerable < Article >> GetArticles(IEnumerable < ArticleCategory > articleCategories, int numArticlesPerCategory) {
throw new NotImplementedException();
}
public async Task < ImportBulkArtifactJob > BuildArticleImportJob(int workspaceID, IEnumerable < Article > articles, TempFileCollection tempFileCollection) {
throw new NotImplementedException();
}
public async Task AddOrUpdateArticleReferences(int workspaceID, IEnumerable < Article > articles) {
throw new NotImplementedException();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public async Task <IEnumerable<int>> GetHelloWikipediaWorkspaceIDs() {
var matchingWorkspaceIDs = new List < int > ();
var queryOptions = new QueryOptions {
Condition = "'Status Code' == 4" // Successful install.
};
const int pageSize = 50;
int currentStart = 1;
ApplicationInstallSearchResponse response;
do {
response = await _applicationInstallManager.SearchApplicationAsync(-1, Constants.HELLO_WIKIPEDIA_APP_GUID, queryOptions, currentStart, pageSize, true);
matchingWorkspaceIDs.AddRange(response.Results.Select(x => x.WorkspaceIdentifier.ArtifactID));
currentStart = response.CurrentStartIndex + pageSize;
}
while (response.NextPage != null && response.NextPage.IsAvailable);
return matchingWorkspaceIDs.Where(id => id != -1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using kCura.Agent;
using kCura.Agent.CustomAttributes;
using kCura.Relativity.DataReaderClient;
using kCura.Relativity.ImportAPI;
using Relativity.API;
using Relativity.ObjectManager.V1.Interfaces;
using Relativity.Services.Interfaces.LibraryApplication;
using WikipediaKepler.Interfaces.WikipediaManagement.v1;
using WikipediaKepler.Interfaces.WikipediaManagement.v1.Models;
using Sort = Relativity.Services.Interfaces.LibraryApplication.Models.Query.Sort;
namespace WikipediaAgent {
[Name("Wikipedia Article Manager Agent")]
[Guid("44ae8827-1c7f-422c-849d-197c1c0f5b68")]
public class WikipediaArticleManagerJob: AgentBase {
private IAPILog _logger;
/// <summary>
/// Returns the name of agent
/// </summary>
public override string Name => "Wikipedia Article Manager Agent";
/// <summary>
/// Agent logic goes here
/// </summary>
public override void Execute() {
_logger = Helper.GetLoggerFactory().GetLogger();
try {
} catch (Exception ex) {
_logger.LogError(ex, "Failed to process Wikipedia pages.");
RaiseError(ex.Message, ex.Message);
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public override void Execute() {
_logger = Helper.GetLoggerFactory().GetLogger();
try {
using (var applicationInstallManager = Helper.GetServicesManager().CreateProxy<IApplicationInstallManager>(ExecutionIdentity.System))
using (var objectManager = Helper.GetServicesManager().CreateProxy<IObjectManager>(ExecutionIdentity.System))
using (var wikipediaService = Helper.GetServicesManager().CreateProxy<IWikipediaService>(ExecutionIdentity.System))
using (var tempFileCollection = new TempFileCollection()) {
ImportAPI importApi = ImportAPI.CreateByRsaBearerToken(Constants.WEB_SERVICE_URL);
var manager = new WikipediaArticleManager(applicationInstallManager, objectManager, wikipediaService, importApi, _logger);
List<int> matchingWorkspaceIDs = manager.GetHelloWikipediaWorkspaceIDs().ConfigureAwait(false).GetAwaiter().GetResult().ToList();
RaiseMessage($"Found Hello Wikipedia in {matchingWorkspaceIDs.Count} workspaces", (int) AgentMessage.AgentMessageType.Informational);
foreach (int workspaceID in matchingWorkspaceIDs) {
IEnumerable<ArticleCategory> articleCategories = manager.GetArticleCategories(workspaceID).ConfigureAwait(false).GetAwaiter().GetResult();
List<Article> articles = manager.GetArticles(articleCategories, Constants.NUMBER_OF_ARTICLES).ConfigureAwait(false).GetAwaiter().GetResult().ToList();
ImportBulkArtifactJob job = manager.BuildArticleImportJob(workspaceID, articles, tempFileCollection).ConfigureAwait(false).GetAwaiter().GetResult();
job.Execute();
job.SourceData.SourceData.Close();
manager.AddOrUpdateArticleReferences(workspaceID, articles).ConfigureAwait(false).GetAwaiter().GetResult();
RaiseMessage($"Processed {articles.Count}", (int) AgentMessage.AgentMessageType.Informational);
}
}
} catch (Exception ex) {
_logger.LogError(ex, "Failed to process Wikipedia pages.");
RaiseError(ex.Message, ex.Message);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public async Task<IEnumerable<ArticleCategory>> GetArticleCategories(int workspaceID) {
var categories = new List<ArticleCategory>();
var queryRequest = new QueryRequest {
ObjectType = new ObjectTypeRef {
Guid = Constants.CATEGORY_OBJECT_TYPE_GUID
},
Fields = new List<FieldRef> {
new FieldRef {
Guid = Constants.CATEGORY_NAME_FIELD_GUID
},
new FieldRef {
Guid = Constants.CATEGORY_OVERWRITE_FIELD_GUID
}
},
Condition = $ "(('{Constants.CATEGORY_UPDATES_FIELD_GUID}' == true))"
};
try {
QueryResult results = await _objectManager.QueryAsync(workspaceID, queryRequest, 1, Constants.NUMBER_OF_ENTRIES_TO_QUERY);
categories.AddRange(results.Objects.Select(obj => new ArticleCategory {
Name = obj[Constants.CATEGORY_NAME_FIELD_GUID]?.Value.ToString(),
OverwriteArticleText = obj[Constants.CATEGORY_OVERWRITE_FIELD_GUID]?.Value as bool ? ?? false
}));
} catch {
_logger.LogWarning("Failed to retrieve article categories for workspace {0}", workspaceID);
}
return categories;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public async Task<IEnumerable<Article>> GetArticles(IEnumerable<ArticleCategory> articleCategories, int numArticlesPerCategory) {
var articles = new List<Article>();
foreach(var category in articleCategories) {
Pageable<PageForCategoryResponseModel> pageablePages = await _wikipediaService.GetPagesForCategoryAsync(category.Name, numArticlesPerCategory);
foreach (PageForCategoryResponseModel page in pageablePages.Results) {
try {
PageResponseModel pageResponseModel = await _wikipediaService.GetPageByNameAsync(page.Title);
articles.Add(new Article {
Category = category.Name, Title = page.Title, Url = pageResponseModel.Url, OverwriteExisting = category.OverwriteArticleText
});
} catch (Exception ex) {
_logger.LogWarning("Failed to retrieve page by name for {0}: ", page.Title);
}
}
}
return articles;
}
Add the following code for these methods:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public async Task<ImportBulkArtifactJob> BuildArticleImportJob(int workspaceID, IEnumerable<Article> articles, TempFileCollection tempFileCollection) {
List<Article> importableArticles = await GetImportableArticles(_objectManager, workspaceID, articles);
ImportBulkArtifactJob job = InitializeImportJob(_importApi, workspaceID);
var dataTable = new DataTable("Wikipedia Agent Import Data") {
Columns = {
new DataColumn("Control Number", typeof (string)),
new DataColumn("Native File", typeof (string))
}
};
foreach (var article in importableArticles) {
string tempArticlePath = Path.ChangeExtension(Path.GetTempFileName(), "html");
tempFileCollection.AddFile(tempArticlePath, keepFile: false);
string pageText = "";
using (var keplerStream = await _wikipediaService.GetPageTextAsync(article.Title)) {
Stream stream = await keplerStream.GetStreamAsync();
var reader = new StreamReader(stream, Encoding.UTF8);
pageText = await reader.ReadToEndAsync();
}
File.WriteAllText(tempArticlePath, pageText);
dataTable.Rows.Add(article.Title, tempArticlePath);
}
job.SourceData.SourceData = dataTable.CreateDataReader();
return job;
}
1
2
3
4
5
6
private string EscapeConditionString(string title) {
string articleTitle = title.Replace("\'", "\\\'"); // Escape ''' Character
articleTitle = articleTitle.Replace("\"", "\\\""); // Escape '"' Character
articleTitle = articleTitle.Replace("\\", "\\\\"); // Escape '\' Character
return articleTitle;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private async Task<List<Article>> GetImportableArticles(IObjectManager objectManager, int workspaceID, IEnumerable<Article> articles) {
Dictionary<bool, List<Article>> overwritableToArticles = articles.GroupBy(article => article.OverwriteExisting).ToDictionary(g => g.Key, g => g.ToList());
List<Article> importableArticles = overwritableToArticles.TryGetValue(true, out
var overwritable) ? overwritable : new List<Article>();
List<Article> nonOverwritableArticles = overwritableToArticles.TryGetValue(false, out
var nonOverwritable) ? nonOverwritable : new List<Article>();
if (nonOverwritableArticles.Any()) {
List<string> conditionElements = new List<string>();
foreach (Article nonOverwriteableArticle in nonOverwritableArticles) {
string articleTitle = EscapeConditionString(nonOverwriteableArticle.Title);
conditionElements.Add($"'{Constants.CONTROL_NUMBER_FIELD_ID}' == '{articleTitle}'");
}
var results = new QueryResult();
var queryRequest = new QueryRequest {
ObjectType = new ObjectTypeRef {
ArtifactTypeID = Constants.DOCUMENT_ARTIFACT_ID
},
Fields = new List<FieldRef> {
new FieldRef {
ArtifactID = Constants.CONTROL_NUMBER_FIELD_ID
}
},
Condition = $ "(({string.Join("
OR ", conditionElements)}))"
};
try {
results = await objectManager.QueryAsync(workspaceID, queryRequest, 1, 1);
} catch {
_logger.LogWarning("Failed to retrieve articles for workspace {1}", workspaceID);
}
foreach (var nonOverwritableArticle in nonOverwritableArticles) {
if (!results.Objects.Any(obj => obj.FieldValues.Any(pair => pair.Value.Equals(nonOverwritableArticle.Title)))) {
importableArticles.Add(nonOverwritableArticle);
}
}
}
return importableArticles;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private static ImportBulkArtifactJob InitializeImportJob(IImportAPI importApi, int workspaceID) {
ImportBulkArtifactJob importJob = importApi.NewNativeDocumentImportJob();
importJob.Settings.ArtifactTypeId = Constants.DOCUMENT_ARTIFACT_ID;
importJob.Settings.Billable = false;
importJob.Settings.BulkLoadFileFieldDelimiter = ";";
importJob.Settings.CaseArtifactId = workspaceID;
importJob.Settings.CopyFilesToDocumentRepository = true;
importJob.Settings.DisableControlNumberCompatibilityMode = true;
importJob.Settings.DisableExtractedTextFileLocationValidation = false;
importJob.Settings.DisableNativeLocationValidation = false;
importJob.Settings.DisableNativeValidation = false;
importJob.Settings.ExtractedTextEncoding = Encoding.Unicode;
importJob.Settings.ExtractedTextFieldContainsFilePath = false;
importJob.Settings.FileSizeColumn = "NativeFileSize";
importJob.Settings.FileSizeMapped = true;
importJob.Settings.FolderPathSourceFieldName = null;
importJob.Settings.IdentityFieldId = Constants.CONTROL_NUMBER_FIELD_ID;
importJob.Settings.LoadImportedFullTextFromServer = false;
importJob.Settings.MaximumErrorCount = int.MaxValue - 1;
importJob.Settings.MoveDocumentsInAppendOverlayMode = false;
importJob.Settings.NativeFileCopyMode = NativeFileCopyModeEnum.CopyFiles;
importJob.Settings.NativeFilePathSourceFieldName = "Native File";
importJob.Settings.OIFileIdColumnName = "OutsideInFileId";
importJob.Settings.OIFileIdMapped = true;
importJob.Settings.OIFileTypeColumnName = "OutsideInFileType";
importJob.Settings.OverwriteMode = OverwriteModeEnum.AppendOverlay;
importJob.Settings.SelectedIdentifierFieldName = "Control Number";
importJob.Settings.StartRecordNumber = 0;
return importJob;
}
1
2
3
4
5
6
7
8
9
10
11
public async Task AddOrUpdateArticleReferences(int workspaceID, IEnumerable<Article> articles) {
foreach (var article in articles) {
RelativityObject maybeArticleReference = await FindExistingArticleReference(_objectManager, workspaceID, article.Title);
if (maybeArticleReference == null) {
await CreateArticleReference(_objectManager, workspaceID, article);
}
else {
await UpdateArticleReference(_objectManager, workspaceID, article, maybeArticleReference);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private async Task<RelativityObject> FindExistingArticleReference(IObjectManager objectManager, int workspaceID, string title) {
var results = new QueryResult();
string articleTitle = EscapeConditionString(title);
var queryRequest = new QueryRequest {
ObjectType = new ObjectTypeRef { Guid = Constants.REFERENCE_OBJECT_TYPE_GUID },
Fields = new List<FieldRef> { new FieldRef { Guid = Constants.REFERENCE_NAME_FIELD_GUID } },
Condition = $"(('{Constants.REFERENCE_NAME_FIELD_GUID}' == '{articleTitle}'))"
};
try {
results = await objectManager.QueryAsync(workspaceID, queryRequest, 1, 1);
}
catch {
_logger.LogWarning("Failed to retrieve article references {0} for workspace {1}", title, workspaceID);
}
return results.Objects.FirstOrDefault();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private async Task CreateArticleReference(IObjectManager objectManager, int workspaceID, Article article) {
RelativityObject articleCategory = await GetArticleCategory(objectManager, workspaceID, article);
var fieldValuePairs = new List<FieldRefValuePair> {
new FieldRefValuePair {
Field = new FieldRef {
Guid = Constants.REFERENCE_NAME_FIELD_GUID
},
Value = article.Title
},
new FieldRefValuePair {
Field = new FieldRef {
Guid = Constants.REFERENCE_URL_FIELD_GUID
},
Value = article.Url
},
new FieldRefValuePair {
Field = new FieldRef {
Guid = Constants.REFERENCE_ARTICLE_CATEGORIES_FIELD_GUID
},
Value = new List<RelativityObject> {
articleCategory
}
}
};
var createRequest = new CreateRequest {
FieldValues = fieldValuePairs,
ObjectType = new ObjectTypeRef {
Guid = Constants.REFERENCE_OBJECT_TYPE_GUID
}
};
await objectManager.CreateAsync(workspaceID, createRequest);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private async Task UpdateArticleReference(IObjectManager objectManager, int workspaceID, Article article, RelativityObject articleReference) {
RelativityObject articleCategory = await GetArticleCategory(objectManager, workspaceID, article);
var fieldValuePairs = new List<FieldRefValuePair> {
new FieldRefValuePair {
Field = new FieldRef {
Guid = Constants.REFERENCE_NAME_FIELD_GUID
},
Value = article.Title
},
new FieldRefValuePair {
Field = new FieldRef {
Guid = Constants.REFERENCE_URL_FIELD_GUID
},
Value = article.Url
},
new FieldRefValuePair {
Field = new FieldRef {
Guid = Constants.REFERENCE_ARTICLE_CATEGORIES_FIELD_GUID
},
Value = new List<RelativityObject> {
articleCategory
}
}
};
var updateRequest = new UpdateRequest {
FieldValues = fieldValuePairs,
Object = new RelativityObjectRef {
ArtifactID = articleReference.ArtifactID
}
};
await objectManager.UpdateAsync(workspaceID, updateRequest);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private async Task<RelativityObject> GetArticleCategory(IObjectManager objectManager, int workspaceID, Article article) {
var results = new QueryResult();
var queryRequest = new QueryRequest {
ObjectType = new ObjectTypeRef { Guid = Constants.CATEGORY_OBJECT_TYPE_GUID },
Fields = new List<FieldRef> { new FieldRef { Guid = Constants.CATEGORY_NAME_FIELD_GUID } },
Condition = $"(('{Constants.CATEGORY_NAME_FIELD_GUID}' == '{article.Category}'))"
};
try {
results = await objectManager.QueryAsync(workspaceID, queryRequest, 1, 1);
}
catch (Exception e) {
_logger.LogWarning(e, "Failed to retrieve article category {0} for workspace {1}", article.Category, workspaceID);
}
return results.Objects.First();
}
In Step 7 - Verify that the agent runs, you will check that your agent retrieves the top 10 articles for article categories in a workspace. They should be retrieved as documents and article references in Relativity.
In this section, you learn how to write a unit test for your new agent.
Review these guidelines for writing unit tests:
Use the following steps to write a unit test:
Rename the UnitTest1.cs file and class name to WikipediaArticleManagerTests.cs and WikipediaArticleManagerTests.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Moq;
using NUnit.Framework;
using Relativity.Services.Interfaces.LibraryApplication;
using Relativity.Services.Interfaces.LibraryApplication.Models;
using Relativity.Services.Interfaces.LibraryApplication.Models.Query;
using Relativity.Services.Interfaces.Shared.Models;
namespace WikipediaAgent.Tests {
[TestFixture]
public class WikipediaArticleManagerTests {
private readonly Random _random = new Random();
[Test]
[Description("Verifies that when GetHelloWikipediaWorkspaceIDs is called, a call is made to the Application Install Manager and matching non-admin workspaces are returned.")]
public async Task GetHelloWikipediaWorkspaceIDs_InstalledInNonAdminWorkspaces_ReturnsMatchingWorkspaceIDs() {
// Arrange
var expectedWorkspaceIDs = new List<int> {
_random.Next()
};
var responseIds = new List<int> {
-1
};
responseIds.AddRange(expectedWorkspaceIDs);
var response = new ApplicationInstallSearchResponse {
Results = responseIds.Select(id => new ApplicationInstallSearchItem {
WorkspaceIdentifier = new DisplayableObjectIdentifier {
ArtifactID = id
}
}).ToList()
};
var applicationInstallManagerMock = new Mock<IApplicationInstallManager> ();
applicationInstallManagerMock.Setup(manager => manager.SearchApplicationAsync(-1, Constants.HELLO_WIKIPEDIA_APP_GUID,
It.Is<QueryOptions> (q => q.Condition.Equals("'Status Code' == 4")), // A status code of 4 indicates a successful installation
It.IsAny<int>(), It.IsAny<int>(), true))
.ReturnsAsync(response);
var articleManager = new WikipediaArticleManager(applicationInstallManagerMock.Object, null, null, null, null);
// Act
IEnumerable<int> actualWorkspaceIDs = await articleManager.GetHelloWikipediaWorkspaceIDs();
// Assert
CollectionAssert.AreEquivalent(expectedWorkspaceIDs, actualWorkspaceIDs);
}
}
}
After confirming that the agent passes the unit tests, you can add the updated agent to your Relativity instance.
Use the following steps to add the updated agent to Relativity:
On this page
Why was this not helpful?
Check one that applies.
Thank you for your feedback.
Want to tell us more?
Great!
Additional Resources |
|||
DevHelp Community | GitHub | Release Notes | NuGet |