Relativity Services API Guide - Relativity Developer Documentation
Transcription
Relativity Services API Guide - Relativity Developer Documentation
Services API Guide Version 8.1 | October 11, 2013 For the most recent version of this document, visit our Relativity 8.1 Developers website. Table of Contents 1 Relativity Services API 9 2 Configuring the Services API 9 2.1 Configuration prerequisites 9 2.2 SSL certificate for HTTPS 10 2.3 SSL certificate for HTTP or Net.TCP 10 2.4 Creating a self-signed certificate 12 2.5 Configuring HTTP or HTTPS connectivity to IIS 13 2.5.1 Verifying HTTP and HTTPS site bindings 14 2.6 Configuring Net.Pipe or Net.TCP connectivity 16 2.6.1 Enabling non-HTTP activation for WCF 16 2.6.2 Configuring the Net.Pipe or Net.TCP protocols 17 2.6.3 Verifying Net.Pipe and Net.TCP site bindings 18 2.6.4 Starting the Net.Pipe or Net.TCP listener adapter service 18 2.7 Manually configuring the Services API for the Relativity platform 18 2.8 Configuration overrides 19 2.8.1 Setting client configuration overrides 19 2.8.2 Setting server configuration overrides 20 2.8.3 Server-side override values 22 2.8.4 Override configuration references 24 2.9 Testing the Services API configuration 25 2.9.1 Relativity Services API Diagnostic Tool 25 2.9.2 Viewing errors in the Services API Diagnostic Tool 28 2.10 Troubleshooting the Services API Configuration 30 2.10.1 Logging and debugging settings in the web.config 31 2.10.2 Production environment trace settings 31 2.10.3 Test environment trace settings 32 2.10.4 FailureAuditing Configuration Override Setting 33 2.10.5 Attempting to access .svc file causes keyset error 33 2.10.6 Catching EndpointType exceptions 34 2.10.7 Connection errors in load-balanced systems 34 2.10.8 Could not load type 'System.ServiceModel.Activation.HttpModule' 34 2.10.9 Erroneous calls to internal web server in load-balanced systems 35 2.10.10 HTTP error 404.17 or 404.3 35 2.10.11 Incorrect version of .NET enabled for application pools 36 Relativity | Services API Guide - 2 2.10.12 Missing handler mappings 36 2.10.13 Pipe endpoint could not be found 38 2.10.14 System.ServiceModel.ExceptionDetail Error 38 2.10.15 Unable to access .svc file path in browser 38 2.10.16 Unable to access website 39 2.10.17 Updating the HTTP host header 39 3 Upgrading the Services API 3.1 Upgrading from Relativity 8 39 39 3.1.1 Upgrade guidelines for the Relativity 8.1 39 3.1.2 Upgrade guidelines from the Relativity 8.0.291.1 patch release 40 3.2 Upgrading from Relativity 7.5 41 3.3 Upgrading from Relativity 7.4 and earlier 42 4 Getting started with the Services API 42 4.1 Prerequisites for a development environment 42 4.2 Services API materials available in the SDK Installer 42 4.3 Setting up a project in Visual Studio 43 4.4 Creating a simple program with the Services API 45 4.4.1 Before you begin 45 4.4.2 Adding directives for required namespaces 46 4.4.3 Adding the Main method 46 4.4.4 Creating the RSAPIClient proxy 47 4.4.5 Querying for a Workspace 48 4.4.6 Creating a Relativity Dynamic Object DTO 49 4.4.7 Creating a Field on a RDO 50 4.4.8 Updating Fields on an RDO 51 4.4.9 Reading an RDO 52 4.4.10 Querying for RDOs 53 4.4.11 Deleting a Field on an RDO 54 4.4.12 Deleting an RDO 54 4.4.13 Exiting the program 55 4.4.14 Writing success and failure messages 55 4.4.15 Building and running the program 56 5 Basic Services API concepts 57 5.1 Services API features 57 5.2 Services API and the Relativity Platform 58 Relativity | Services API Guide - 3 5.3 Client Proxy 5.3.1 Backwards Compatibility 5.4 Deploying the Services API 59 59 60 5.4.1 Deploying the Services API 60 5.4.2 Transport protocols 60 5.4.3 AuthenticationType classes supported by the RSAPIClient 60 5.4.4 Summary of authentication methods and endpoint types 61 5.5 Creating the RSAPIClient proxy 61 5.5.1 RSAPIClient class overview 61 5.5.2 APIOptions class 62 5.5.3 RSAPIClientSettings class 62 5.5.4 RSAPIClientServiceOperationFailed event 62 5.5.5 Best practices for proxy creation 62 5.5.6 Creating the proxy in an agent, custom page, or event handler 63 5.5.7 Code samples for proxy creation in a console application 63 5.6 Token login 65 5.6.1 Creating the proxy using a token 65 5.6.2 Generating an authorization token for Relativity 66 5.7 Data Transfer Objects (DTOs) 67 5.7.1 DTO Features 67 5.7.2 Supported DTOs 68 5.8 Untyped base Artifacts 69 5.8.1 RSAPIClient support for ArtifactTypes 69 5.8.2 Limited support for Create() on Fields 70 5.8.3 Field (untyped) 70 5.9 StrictMode property and Field directives 73 5.9.1 StrictMode property for DTOs 73 5.9.2 Field directives for DTOs 73 5.9.3 StrictMode and the TextIdentifer property (untyped layer) 74 5.9.4 Field directives (untyped layer) 74 5.10 Best practices for the Services API 75 5.10.1 Use DTOs whenever possible 75 5.10.2 Bring back only Fields that you need for optimum performance 75 5.10.3 Don’t use the Services API to bulk-load data 75 5.10.4 Work in batches 75 Relativity | Services API Guide - 4 5.10.5 Use GUIDs to reference Fields and object types 75 5.10.6 Install and uninstall applications through the ADS 76 5.10.7 Use of the APIOptions token property 76 5.10.8 Avoid specific version assembly references 76 5.10.9 Use constant Field names for Read() and Query()methods 77 5.10.10 Use Services API enumerations or constants 77 5.11 GUIDs in application development 78 5.11.1 Using GUIDs as best practice 78 5.11.2 Using GUIDs in read, update, and delete operations 78 5.11.3 Viewing GUIDs for your application components 78 5.12 Asynchronous framework 78 5.13 Terminology 79 6 DTO reference and code samples 6.1 Batch 81 82 6.1.1 Updating and reading a Batch 82 6.1.2 Querying for a Batch 84 6.2 BatchSet 85 6.2.1 Creating, updating, and querying BatchSet objects 85 6.2.2 Creating and deleting a BatchSet 88 6.2.3 Creating Batches for a BatchSet 90 6.2.4 Querying for a Batch Set 91 6.2.5 Canceling the creation of a BatchSet 91 6.2.6 Purging Batches for a BatchSet 93 6.2.7 Event handlers used in code samples 94 6.3 Choice 95 6.3.1 Reading a Choice 95 6.3.2 Querying on a Choice 96 6.4 Client 97 6.4.1 Creating a Client 97 6.4.2 Reading a Client 98 6.4.3 Updating a Client 100 6.4.4 Deleting a Client 101 6.4.5 Querying for a Client 102 6.5 Document 6.5.1 Fields on a Document DTO Relativity | Services API Guide - 5 103 103 6.5.2 Creating a Document 103 6.5.3 Reading a Document 105 6.5.4 Updating a Document 106 6.5.5 Deleting a Document 108 6.5.6 Querying for a Document 109 6.5.7 Downloading a native file 111 6.6 Error 6.6.1 Writing to the error log 6.7 Field 112 112 113 6.7.1 System type fields 113 6.7.2 Field types 114 6.7.3 Supported operations for Field types 128 6.7.4 Field code samples 128 6.7.5 File field 135 6.7.6 Fields used by Field type, User, and Group 141 6.7.7 Constant Field names 146 6.8 Folder 153 6.8.1 Creating a Folder 153 6.8.2 Reading a Folder 154 6.8.3 Deleting a Folder 155 6.8.4 Querying for a Folder 157 6.9 Group 158 6.9.1 Creating a Group 158 6.9.2 Reading a Group 159 6.9.3 Updating a Group 160 6.9.4 Deleting a Group 161 6.9.5 Querying for a Group 162 6.10 Layout 163 6.10.1 Reading a Layout 164 6.10.2 Querying for a Layout 165 6.11 MarkupSet 166 6.11.1 Creating a MarkupSet 166 6.11.2 Reading a MarkupSet 167 6.11.3 Updating a MarkupSet 168 6.11.4 Deleting a MarkupSet 169 Relativity | Services API Guide - 6 6.11.5 Querying for a MarkupSet 6.12 ObjectType 170 172 6.12.1 Creating an ObjectType 172 6.12.2 Reading an ObjectType 173 6.12.3 Updating an ObjectType 174 6.12.4 Deleting an ObjectType 175 6.12.5 Querying for an ObjectType 176 6.13 RDO 177 6.13.1 Creating an RDO 177 6.13.2 Creating an RDO as a child object 179 6.13.3 Reading an RDO 180 6.13.4 Updating an RDO 182 6.13.5 Deleting an RDO 183 6.13.6 Querying for an RDO 184 6.14 RelativityApplication 186 6.14.1 Reading a RelativityApplication 186 6.14.2 Deleting a Relativity Application 187 6.14.3 Querying for a RelativityApplication 188 6.15 RelativityScript 189 6.15.1 Reading a RelativityScript 190 6.15.2 Querying for a RelativityScript 191 6.15.3 Executing a RelativityScript 192 6.15.4 Retrieving Input for a RelativityScript 195 6.16 Tab 196 6.16.1 Reading a Tab 196 6.16.2 Querying for a Tab 197 6.17 User 198 6.17.1 Creating a User 198 6.17.2 Utility methods used in User DTO creation 201 6.17.3 Reading a User 203 6.17.4 Updating a User 204 6.17.5 Deleting a User 206 6.17.6 Querying for a User 206 6.18 View 6.18.1 Reading a View Relativity | Services API Guide - 7 207 208 6.18.2 Querying for a View 6.19 Workspace 209 210 6.19.1 Reading a Workspace 210 6.19.2 Querying for a Workspace 211 7 Additional Services API functionality 7.1 File transfers 212 212 7.1.1 Supported file transfer operations 212 7.1.2 File transfer error messages 213 7.1.3 Error classes 213 7.1.4 Sample error handling code 214 7.2 Mass processes 7.2.1 Mass delete operations 7.3 Querying 215 223 227 7.3.1 Using Query objects 227 7.3.2 Available Conditions for Querying 227 7.3.3 System Types supported by the Query() method 228 7.3.4 Paging 228 7.3.5 Constraints on the Query() method 229 7.3.6 SavedSearchCondition 229 7.3.7 Specialized queries with Conditions 231 7.3.8 ViewCondition 233 8 Troubleshooting the Services API 236 8.1 Common causes of Services API errors 236 8.2 Error occurs when machines in a workgroup attempt to log in 237 Relativity | Services API Guide - 8 1 Relativity Services API The Services API supports the development of customized end-user applications that interact with Relativity. It simplifies the development process by providing object classes and other data structures that are the building blocks for custom applications. 2 Configuring the Services API The Services API is installed on the Relativity web server as an additional IIS application called Relativity.Services. When you install Relativity on your web server, the installer automatically configures the Services API to use Net.Pipe. Note: You don’t need to complete any additional configuration steps for the Services API unless you want to use HTTP, HTTPS, or TCP as the scheme, or customize the client and server configuration settings. You may also need to configure Net.Pipe on the IIS if you ran the Relativity installer before rebooting your machine after Windows updates were installed. Most of the configuration of Relativity.Services is determined by the settings in the web.config file of the Relativity.Services virtual directory. A few of the configuration options are controlled in IIS. The configuration process includes configuring settings in IIS, testing the connectivity to the Services API, and troubleshooting the configuration settings. 2.1 Configuration prerequisites Before you begin configuring the Services API, you will need to complete the following tasks: n n Confirm that Relativity has been installed in your environment, and that the Services API has been installed as part of the web server component of Relativity. Determine the scheme that you want to use, such as HTTP, HTTPS, or TCP. You will also need to select an authentication method, such as username/password or Windows credentials. The Services API can use a different authentication scheme than other Relativity components installed on the same web server. Note: You don't need to complete any configuration steps if you use Net.Pipe as installed on the IIS when you run the Relativity installer. n Obtain a valid SSL certificate based on these requirements: o o HTTPS - If you're using this scheme, you must have a certificate. Username/password over HTTP or username/password over Net.TCP - If you're defining the CertificateFindValue in the web.config file on the server, you need a certificate for these schemes. Otherwise, you don't need a certificate. Note: Make sure that the Subject Name on the certificate is unique. Relativity | Services API Guide - 9 2.2 SSL certificate for HTTPS You need to obtain a SSL certificate if you are going to use HTTPS. 1. Obtain a digital certificate that meets the following requirements: Make sure that you have a trusted certificate. You can use a certificate issued from a valid certificate authority or a self-signed certificate. n Matches the domain where Relativity Services is installed n Must be current (You can’t use an expired certificate.) 2. Ensure there is a properly configured HTTPS binding. See Configuring HTTP or HTTPS connectivity to IIS on page 13. n 2.3 SSL certificate for HTTP or Net.TCP If you're defining the CertificateFindValue in the web.config file on the server, you need a certificate for these authentication schemes: n n Username/password over HTTP Username/password over Net.TCP Note: If you aren't setting CertificateFindValue in the web.config file, you don't need to complete the following steps. Complete the following steps to configure an SSL certificate for username/password over HTTP. 1. Create or import a certificate. Use the instructions available on the Microsoft website for one of the following methods: n n Import a certificate from another source into IIS 7 (http://technet.microsoft.com/en-us/library/cc732785(v=ws.10).aspx) Create a self-signed certificate in IIS 7 (http://technet.microsoft.com/en-us/library/cc753127 (v=ws.10).aspx) Note: If you use another method to install the certificate, add it to the Personal store for the local machine but not the Personal store for the current user. (.NET refers to this location as My store.) You will need the Subject Name value on the certificate when you configure IIS. 2. Complete the following steps to configure the server to use the certificate: a. Locate the web.config file for the Services API. The default directory is listed below: <YourInstallationDirectory>\kCura Corporation\Relativity\Relativity.Services b. In this file, set the value for the CertificateFindValue key to the Subject value on the certificate. See the following example: <kCura.CommonServiceValues.Config> Relativity | Services API Guide - 10 <add key="CertificateFindValue" value="myServer.kcura.com"/> </kCura.CommonServiceValues.Config> As displayed in IIS, the value myServer.kcura.com matches the value for the Subject in the certificate: Note: Make sure that the Subject value (or name) is unique in your environment. When multiple certificates with the same name exist, the Services API can’t determine the correct one to use. You can view certificates in IIS, which displays the Subject in the Issued To column on Server Certificates pane. c. (Optional) Override specific configuration settings in the web.config file as required for your environment. See Configuration overrides on page 19. 3. Complete the following steps to configure the client to use the certificate: a. Ensure that the CertificateFindValue corresponds to the value for the CertificateFindValue key specified in the web.config file on the server. (A CertificateFindValueInvalidException is thrown when a mismatch occurs between the CertificateFindValue defined on the server and the client.) b. Determine if you need to set the CertificateFindValue. If this value is set on either the client or the server, then you must configure it on the other entity. (You can't configure only one of these entities. If the CertificateFindValue on the client is set, then you must configure it on the server, and vice versa.) See Configuration overrides on page 19. Relativity | Services API Guide - 11 Note: The client won't validate the certificate by default. However, you can control this behavior by updating the CertificateValidation setting. 4. Configure IIS to require SSL on the Relativity.Services virtual directory. See Configuring HTTP or HTTPS connectivity to IIS on the next page. 5. Confirm endpoint connections. See Testing the Services API configuration on page 25. 2.4 Creating a self-signed certificate For development purposes, you can create self-signed certificates with a utility included in the . The Self Signed Certificate Creator is available Tools folder of your Relativity SDK installation. Note: Don't use the certificates created with this utility in your production environment. These certificates are for development purposes only. 1. Locate Tools folder of your Relativity SDK installation. The following path is default location: ...\Program Files\kCura Corporation\Relativity SDK\RSAPI\Tools 2. Click CertificateCreatorForm.exe to open the application. 3. Enter the following information required to create a certificate: n n Friendly Name - Enter a name for the certificate that you want to use for identification purposes. The Friendly Name is used for your convenience to make it easier to distinguish between different certificates. Machine Name - Enter the name of the machine that will be used as the Subject field on the certificate. Use a value that is appropriate for a self-signed certificate. You can update this value, if necessary. This value will also be displayed in the Issued To column. Relativity | Services API Guide - 12 Note: If you are unsure about how to set the Machine Name, don't use this tool to create a certificate. # Years Valid - Enter the number of years from the creation date that you want the certificate to be valid. 4. Click Create Certificate. You will see your new certificate listed in the Current Certificates box. This list includes the same set of certificates available on IIS. It is populated from information in the Local Machine\Personal certificate store in Windows. n 2.5 Configuring HTTP or HTTPS connectivity to IIS You must configure IIS hosting Relativity.Services to use anonymous authentication, since the authentication occurs within Relativity rather than on the web server itself. 1. On the web server, log on as a member of the Administers group. 2. Open IIS Manager. 3. Under the Sites node, select the Relativity.Services virtual directory. 4. Double-click on Authentication. 5. Verify that only Anonymous Authentication is enabled. The Services API requires that only Anonymous Authentication is enabled, and that all other authentication methods are disabled. However, you can use the other authentication methods for other Relativity components. Relativity | Services API Guide - 13 6. If you are using HTTPS with username/password or HTTPS with Windows authentication, verify that HTTPS binding is configured on the Relativity.Services virtual directory. See Verifying HTTP and HTTPS site bindings below. Note: You must have an SSL certificate installed and configured for the website that is trusted by calling clients. It must be installed at the website level. 7. If you are using HTTP with username/password authentication, complete the following steps to disable SSL on the Relativity.Services virtual directory: a. Click the SSL Settings option for the Relativity.Services virtual directory. b. Double-click on SSL Settings. c. Verify that Require SSL is cleared. 8. Complete the steps in Testing the Services API configuration on page 25. 2.5.1 Verifying HTTP and HTTPS site bindings Use these steps to verify the site bindings for the HTTP or HTTPS protocols that you have configured on your server. Relativity | Services API Guide - 14 1. On the web server, open IIS Manager. 2. In IIS Manager Connections pane, expand Sites. 3. Right - click on Default Web Site. 4. In the right-click menu, click Edit Bindings to display the Site Bindings dialog. 5. Verify the information for the HTTP or HTTPS protocol that you are using. For HTTPS, you will need to ensure that the SSL certificate is valid. Click Edit to view the SSL certificate assigned to the site binding. Relativity | Services API Guide - 15 6. Click Close. 2.6 Configuring Net.Pipe or Net.TCP connectivity You can configure the following types of connections to IIS: n n n Net.Pipe with Windows authentication Net.TCP with username/password authentication(If you are using Net.TCP with username/password authentication and defining the CertificateFindValue in the web.config file, you must install an SSL certificate. See SSL certificate for HTTP or Net.TCP on page 10.) Net.TCP with Windows Authentication Note: When you install Relativity on your web server, the installer automatically configures the Services API to use Net.Pipe. However, you may need to configure Net.Pipe on the IIS if you ran the Relativity installer before rebooting your machine after Windows updates were installed. 2.6.1 Enabling non-HTTP activation for WCF Use these steps to set the Non-HTTP Activation option in the Windows Server Manager: 1. 2. 3. 4. On the web server, log on as a member of the Administers group. In the Control Panel, open Windows Server Manager. Open the Add Features Wizard. Under WCF Activation, select Non-HTTP Activation. Relativity | Services API Guide - 16 5. Complete the steps in Configuring the Net.Pipe or Net.TCP protocols below. 2.6.2 Configuring the Net.Pipe or Net.TCP protocols You can configure Net.Pipe or Net.TCP protocols by using a binding configuration. For more information, see NamedPipe Activation (http://msdn.microsoft.com/en-us/library/ms752253.aspx). 1. 2. 3. 4. 5. On the web server, log on as a member of the Administers group. Open IIS Manager. Under the Sites node, expand the Default Web Site. Right-click on the Relativity.Services virtual directory. Point to Manage Application and click Advanced Settings. 6. Add the Net.Pipe or Net.TCP protocol to the Enabled Protocols box, if necessary. Relativity | Services API Guide - 17 7. Click OK. 8. Complete the steps in Verifying HTTP and HTTPS site bindings on page 14. 2.6.3 Verifying Net.Pipe and Net.TCP site bindings Use these steps to verify the site bindings for the Net.Pipe or Net.TCP protocols that you have configured on your server. 1. 2. 3. 4. On the web server, open IIS Manager. In IIS Manager Connections pane, expand Sites. Click Default Web Site. In the Actions pane, click Bindings to display the Site Bindings dialog. 5. Verify the information for the protocol that you are using: Net.Pipe Protocol - Only one net.pipe entry exists in the list of binding types. Net.TCP Protocol - Only one net.tcp entry exists in the list of binding types. 6. Click Close. 7. Complete the steps in Starting the Net.Pipe or Net.TCP listener adapter service below. n n 2.6.4 Starting the Net.Pipe or Net.TCP listener adapter service Use these steps to start the service: 1. On the web server, open Services from Administrative Tools. 2. Verify that the Net.Pipe Listener Adapter or the Net.Tcp Listener Adapter service is running. If not, right-click on the service and click Start. 3. Complete the steps in Testing the Services API configuration on page 25. 2.7 Manually configuring the Services API for the Relativity platform If you need to configure the Services API manually, use these guidelines to set up components available with the Relativity 8.1 platform. Agents n n Services API Hosting - Self-hosted or on IIS (if available on your server) Relativity.Services URL - Use method on Relativity API Helpers to return URL. Relativity | Services API Guide - 18 n n Transport Protocol - Net.Pipe Authentication method - We recommend Windows authentication over token authentication, which has increased overhead. Custom pages or event handlers (except Pre and Post Install) n n n n Services API Hosting - Locally through IIS. The Relativity installer automatically adds the Services API to the web server. (Pre and Post Install event handlers don’t require a connection to the Services API.) Relativity.Services URL - Use method on Relativity API Helpers to return URL. Transport Protocol - Net.Pipe Authentication method – Use Windows authentication for full access as the service account or use token authentication for an audited, secure, and user-aware context. Pre and Post Install event handlers n n n n Services API Hosting - Self-hosted or on IIS (The method for hosting the Services API depends on whether an agent or Procuro runs the Pre and Post event handlers, or whether they are run from the web server.) Relativity.Services URL - Use method on Relativity API Helpers to return URL. Transport Protocol - Net.Pipe Authentication method – Use Windows authentication for full access as the service account. Applications Services API Hosting - Host remotely. Note: Due to certificate issues, the Services API shouldn't be hosted remotely for agents, custom pages, and event handlers. If you must use this configuration, use HTTPS as the transport protocol. In addition, use Windows authentication for full access as a service, or use Active Directory authentication by calling the LoginWithCredentials() method, and passing a username and password. 2.8 Configuration overrides You can use configuration overrides to enter custom values for endpoint configurations. In general, you will probably not need to override the default configuration values. 2.8.1 Setting client configuration overrides Client configuration settings override single values. However, they don’t override all configuration values as a custom service and endpoint configuration would. You can programmatically apply configuration overrides in a client-side application by using the RSAPIClientSettings class. The available client configuration overrides share the same name as server-side configuration overrides. Even though the names are the same, these values apply only to either the server or the client. Configuration issues may occur if a value configured on the client-side conflicts with that configured on the server-side, or vice versa. Ensure that the configuration override values on the client and server match. To modify configuration settings programmatically, override the properties available on the RSAPIClientSettings class. The following code sample illustrates how to set a CertificateFindValue value: RSAPIClientSettings settings = new RSAPIClientSettings(); Relativity | Services API Guide - 19 settings.CertificateFindValue = "myServer.myCompany.com"; The following list includes the available override settings for the client: CertificateFindValue n n Datatype - String Description - Specifies the Subject Name value used to identify the server-side certificate. This clientside value must match the server-side value exactly. The value is case-sensitive. The default value is String.Empty. Note: This setting applies only to username/password over HTTP and username/password over Net.TCP endpoint types. CertificateValidation n n Datatype - Boolean Description - Determines whether the client validates the server certificate that the CertificateFindValue specifies. Note: This setting applies only to username/password over HTTP and username/password over Net.TCP endpoint types. 2.8.2 Setting server configuration overrides You can configure single value or service-wide overrides on the server hosting the Services API. You can use single override values for all 4 of the services: Authentication, DataManipulation, FileTransfer, and SetExecutor. These override values are configured in the kCura.Authentication.Config, kCura.DataManipulation.Config, kCura.FileTransfer.Config, and kCura.SetExecutor.Config sections, respectively. An additional configuration section called kCura.CommonServiceValues.Config contains configuration values that apply equally to all services. 1. Navigate to the Services API web.config in the following directory on the server: <YourInstallationDirectory>\kCura Corporation\Relativity\Relativity.Services 2. Open the web.config in a text or other editor. 3. Locate the <configSections>…</configSections> tags in the file. 4. Define the configuration override settings for each of the configuration sections between these tags. See the following example: <configSections> <section name="kCura.Config" type="System.Configuration.DictionarySectionHandler, System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> <section name="kCura.Authentication.Config" type="System.Configuration.DictionarySectionHandler, System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> Relativity | Services API Guide - 20 <section name="kCura.DataManipulation.Config" type="System.Configuration.DictionarySectionHandler, System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> <section name="kCura.FileTransfer.Config" type="System.Configuration.DictionarySectionHandler, System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> <section name="kCura.SetExecutor.Config" type="System.Configuration.DictionarySectionHandler, System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> <section name="kCura.CommonServiceValues.Config" type="System.Configuration.DictionarySectionHandler, System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> <section name="customBasicAuthentication" type="Thinktecture.CustomBasicAuthentication.CustomBasicAuthenticationSection, Thinktecture.CustomBasicAuthenticationModule"/> </configSections> <kCura.Config> <add key="encryptedConnectionString" value="…"/> </kCura.Config> <kCura.CommonServiceValues.Config> <add key="ExceptionDetailsInFaults" value="true"/> </kCura.CommonServiceValues.Config> <kCura.Authentication.Config> </kCura.Authentication.Config> <kCura.DataManipulation.Config> </kCura.DataManipulation.Config> <kCura.FileTransfer.Config> </kCura.FileTransfer.Config> <kCura.SetExecutor.Config> </kCura.SetExecutor.Config> 5. Add override settings under configuration sections as necessary: <add key=”OverrideName” value=”OverrideValue”/> Replace OverrideName and OverrideValue with the appropriate override key and value as illustrated below: <add key=”ExceptionDetailsInFaults” value=”false”/> Relativity | Services API Guide - 21 Note: An error will be thrown if a key exists in a section of the configuration file where it doesn't belong. For example, ExceptionDetailsInFaults is only valid in kCura.CommonServiceValues.Config, so an error will be thrown if it is incorrectly placed in kCura.Authentication.Config. Verify that you don't have any typographical errors in the key. Keys are case sensitive. 2.8.3 Server-side override values The following list includes the available override settings for each service. CertificateFindValue n n n Datatype - String Description - Username/password over HTTP or username/password over Net.TCP endpoint configurations requires a certificate to secure the contents of the message. The location of this certificate is specified as the local machine, while it is the store titled My that is searched. The CertificateFindValue specifies the value to look for in the Subject Name of the certificate. For example, if you used IIS to create a self-signed certificate on a machine residing at myMachine.domain.corp, then specify myMachine.domain.corp as the CertificateFindValue. This value is case-insensitive. The default value is String.Empty for both services. Sections - kCura.CommonServiceValues.Config Note: This setting applies only to username/password over HTTP and username/password over Net.TCP endpoint types. ExceptionDetailsInFaults n n n Datatype - bool Description - Determines how to report exceptions to the client. When set to false (default), detailed exception information isn't reported to the client. When set to true (enabled), the server sends detailed information. The recommendation is to enable this flag only for debugging during development. The default value is False. Sections - kCura.CommonServiceValues.Config FailureAuditing n n n Datatype - bool Description - Set this value to True if you want to audit failed security events from WCF. Audited security events include transport, message, or negotiate authentication and authorization events. You can view these events in the Windows Event Viewer, since they are written to Windows event log. The default value is True. Sections - kCura.CommonServiceValues.Config MaxArrayLength n n n Datatype - Integer Description - Determines the maximum array length created and returned at various stages of message processing. The default value for FileTransfer is Int.MaxValue, and for all other services, it is 1000000. Sections - kCura.Authentication.Config, kCura.DataManipulation.Config, kCura.FileTransfer.Config, kCura.SetExecutor.Config MaxBufferSize n Datatype - Integer Relativity | Services API Guide - 22 n n Description - Specifies the maximum buffer size (in bytes) used to store messages in memory. If more data is received than can fit in the buffer, it remains on the underlying socket until there is enough space. This value should match the MaxReceivedMessageSize. The default value for FileTransfer is Int.MaxValue, and for all other services, it is 104857600. Sections - kCura.Authentication.Config, kCura.DataManipulation.Config, kCura.FileTransfer.Config, kCura.SetExecutor.Config MaxBufferPoolSize n n n Datatype - Long Description - Specifies the maximum buffer pool size (in bytes) used by the BufferManager. As messages are received, buffers are required to process them as they come out of the channel. If the BufferManager has insufficient memory, additional memory must be allocated. The default value for FileTransfer is Int.MaxValue, and for all other services, it is 104857600. Sections - kCura.Authentication.Config, kCura.DataManipulation.Config, kCura.FileTransfer.Config, kCura.SetExecutor.Config MaxChunkSize n n n Datatype - Long Description - Specifies the maximum size of each chunk (in bytes) used during a file transfer operation. Since the client requests a chunk size when starting an upload or download, MaxChunkSize sets an upper limit. This value used as the chunk size if the client doesn't specify a size. Larger chunk sizes require fewer messages to transfer a file. If a message must be sent again, it will take longer amount of time as compared to a smaller message. If this value is smaller than MinChunkSize, MinChunkSize is treated as the maximum and this value becomes the minimum. The default value is 262144. Section - kCura.FileTransfer.Config MinChunkSize n n n Datatype - Long Description - Specifies the minimum size of each chunk (in bytes) used during a file transfer operation. Since the client requests a chunk size when starting either an upload or download, MinChunkSize sets a lower limit. Smaller chunk sizes take less time to re-send in the case of a temporary failure, and also result in the need to send more messages. If this value is larger than MaxChunkSize, MaxChunkSize becomes the new minimum and this value becomes the maximum. The default value is 8192. Section - kCura.FileTransfer.Config MaxConcurrentCalls n n n Datatype - Integer Description - Specifies the maximum number of messages allowed to be processed at any given time by a particular ServiceHost instance. The default value is 100. Sections - kCura.Authentication.Config, kCura.DataManipulation.Config, kCura.FileTransfer.Config, kCura.SetExecutor.Config MaxConcurrentInstances n n n Datatype - Integer Description - Specifies the maximum number of instances of the service. If messages arrive while this limit is reached, the messages are held until resources are available. The default value is 200 for all services. Sections - kCura.Authentication.Config, kCura.DataManipulation.Config, kCura.FileTransfer.Config, kCura.SetExecutor.Config Relativity | Services API Guide - 23 MaxConcurrentSessions n n n Datatype - Integer Description - Specifies the maximum number of service sessions. This value should match MaxConcurrentInstances, and should be set to approximately 100 * # of processors on the server. The default value is 200 for all services. Sections - kCura.Authentication.Config, kCura.DataManipulation.Config, kCura.FileTransfer.Config, kCura.SetExecutor.Config MaxDepth n n n Datatype - Integer Description - Specifies the maximum node depth. The default value is 200. Sections - kCura.Authentication.Config, kCura.DataManipulation.Config, kCura.FileTransfer.Config, kCura.SetExecutor.Config MaxReceivedMessageSize n n n Datatype - Long Description - Specifies the maximum size of a message (in bytes) that will be processed by the service. This value helps limit DoS attacks, and should be large enough to accommodate larger message payloads. The default value for FileTransfer is Int.MaxValue, and for all other services, it is 104857600. Sections - kCura.Authentication.Config, kCura.DataManipulation.Config, kCura.FileTransfer.Config, kCura.SetExecutor.Config MaxStringContentLength n n n Datatype - Integer Description - Specifies the maximum string length returned by the XML reader. The default value for FileTransfer is Int.MaxValue, and for all other services, it is 104857600. Sections - kCura.Authentication.Config, kCura.DataManipulation.Config, kCura.FileTransfer.Config, kCura.SetExecutor.Config ReceiveTimeout n n n Datatype - Timespan Description - Specifies the length of time a connection can be inactive before it is dropped. ReceiveTimeout specifies how long a service waits from the beginning of a receiving a request until the message is finished being processed. The default value is 10 minutes for all services. Sections - kCura.Authentication.Config, kCura.DataManipulation.Config, kCura.FileTransfer.Config, kCura.SetExecutor.Config 2.8.4 Override configuration references For more information about override configuration options, review the following references: n IncludeExceptionDetailInFaults Property: n http://msdn.microsoft.com/enus/library/system.servicemodel.servicebehaviorattribute.includeexceptiondetailinfaults.aspx MaxArrayLength Property: n http://msdn.microsoft.com/en-us/library/system.xml.xmldictionaryreaderquotas.maxarraylength (v=vs.100).aspx MaxBufferSize Property: http://msdn.microsoft.com/en-us/library/system.servicemodel.basichttpbinding.maxbuffersize.aspx Relativity | Services API Guide - 24 n MaxBufferPoolSize Property: n http://msdn.microsoft.com/enus/library/system.servicemodel.basichttpbinding.maxbufferpoolsize.aspx MaxConcurrentCalls Property: n http://msdn.microsoft.com/enus/library/system.servicemodel.description.servicethrottlingbehavior.maxconcurrentcalls.aspx MaxConcurrentInstances Property: n http://msdn.microsoft.com/enus/library/system.servicemodel.description.servicethrottlingbehavior.maxconcurrentinstances.aspx MaxConcurrentSessions Property: n http://msdn.microsoft.com/enus/library/system.servicemodel.description.servicethrottlingbehavior.maxconcurrentsessions.aspx MaxDepth Property: n http://msdn.microsoft.com/en-us/library/system.xml.xmldictionaryreaderquotas.maxdepth.aspx MaxReceivedMessageSize Property: n http://msdn.microsoft.com/enus/library/system.servicemodel.wshttpbindingbase.maxreceivedmessagesize.aspx MaxStringContentLength Property: n http://msdn.microsoft.com/enus/library/system.xml.xmldictionaryreaderquotas.maxstringcontentlength.aspx ReceiveTimeout Property: n http://msdn.microsoft.com/en-us/library/system.servicemodel.channels.binding.receivetimeout.aspx SendTimeout Property: http://msdn.microsoft.com/en-us/library/system.servicemodel.channels.binding.sendtimeout.aspx 2.9 Testing the Services API configuration The Relativity Services API Diagnostic Tool provides you with the ability to test the configuration of the Services API in your environment. It displays error messages with troubleshooting information when it detects incorrect settings for certificate values, and other endpoint configuration errors. 2.9.1 Relativity Services API Diagnostic Tool You can use the Relativity Services API Diagnostic Tool to verify that the Services API is configured properly in your environment. This tool tests the WSDL endpoint configuration for the RSAPIClient proxy, and tests all services that it runs in the background. The tool displays error messages when it detects a problem. See Viewing errors in the Services API Diagnostic Tool on page 28. 1. Navigate to the following DiagnosticTool folder in your installation directory, and click ProxyDiagnostic.exe. For example, you would use the following path if the SDK was installed on drive C: C:\Program Files\kCura Corporation\Relativity SDK\RSAPI\Tools\DiagnosticTool\ ProxyDiagnostic.exe 2. To launch the Services API Diagnostic Tool, click the executable. Relativity | Services API Guide - 25 3. Enter Username and Password that you are using for authentication. Leave these fields blank if you aren't using this type of authentication. 4. Enter the name of Server where the Services API is running. (The Services API Diagnostic Tool only requires the machine name, but you can enter the URI for the server, and it will parse the name from this string.) 5. In the Authentication tree, select the authentication methods that you want to test. You can right-click on this list to select or clear all the check boxes. By default, all of the authentication methods are selected. You may initially want to test all methods to identify, which endpoint types are available in your environment. You can then disable any endpoint types that you don't want exposed. 6. (Optional) If a CertificateFindValue is defined in the web.config for your server, select the Auto-Detect CertificateFindValue box. The Services API Diagnostic Tool will automatically populate the CertificateFindValue box with the current server setting. See Server-side override values on page 22. Note: Leave the Auto-Detect CertificateFindValue box blank if you aren't setting the CertificateFindValue in the web.config file. 7. Click Run Diagnostics. The Current EndPointType box is updated with status messages as the test proceeds. The Services API Diagnostic Tool automatically detects the CertificateFindValue for the server. Relativity | Services API Guide - 26 8. Review the results of the endpoint tests in the Authentication tree: Green Status - indicates the endpoint type is available and the connection succeeded. Red Status - indicates that the attempt to connect with this endpoint failed. To obtain more information about a failure, complete step 9. 9. (Optional) Complete the following tasks if you want to view the exception thrown when the endpoint test failed: n n a. Select only the failed endpoint in the Authentication tree. b. Select the Display ServiceOperationFailed Events option. c. Click Run Diagnostics to display exception details including the exception thrown and the stack trace as in the following illustration. Relativity | Services API Guide - 27 Note: You can also perform additional troubleshooting to resolve these errors. See Viewing errors in the Services API Diagnostic Tool below and Troubleshooting the Services API Configuration on page 30. d. (Optional) Click Copy to Clipboard if you want copy the error messages generated in the Current EndpointType box. 2.9.2 Viewing errors in the Services API Diagnostic Tool The RelativityServices API Diagnostic Tool displays error messages when it detects that the Services API isn't configured properly. You can review the details for these error messages and then use the suggested resolution to update the endpoint configuration for the RSAPIClient proxy. See Configuring the Services API on page 9. 2.9.2.1 Ambiguous CertificateFindValue defined You will receive the following error message when the CertificateFindValue needs to be more specific: The value '<VALUE>' was specified as the server-side CertificateFindValue, but there is more than one matching certificate. Remove certificates with duplicate SubjectName values from the Certificate Store, or provide a more specific SubjectName. This error occurs when the supplied CertificateFindValue in the server-side web.config file needs to be more specific. (See '<VALUE>' in the message.) If the name supplied for the CertificateFindValue matches multiple certificates in IIS, you must update the CertificateFindValue to identify a unique certificate name, or remove the duplicate certificates. See Configuration overrides on page 19. Relativity | Services API Guide - 28 2.9.2.2 No CertificateFindValue defined You will receive the following error message when no value was supplied as the server-side CertificateFindValue: Nothing was specified as the server-side CertificateFindValue. Provide the SubjectName value of a valid certificate in the Certificate Store. This error occurs when no value was defined for the CertificateFindValue, or an empty string ("") was used. Add an entry similar to the following example to the kCura.CommonServiceValues.config section of the Relativity.Services web.config file: <add key=”CertificateFindValue” value=”myCert.subjectName.com” /> 2.9.2.3 Invalid CertificateFindValue defined You will receive the following error message when an invalid value was supplied as the server-side CertificateFindValue: The value '<VALUE>' was specified as the server-side CertificateFindValue, but a matching certificate couldn't be found. Double check the name and spelling and ensure the certificate is in the correct Certificate Store. This error occurs when the value supplied as the CertificateFindValue doesn't match any existing certificate. Ensure the certificate is in the appropriate store, and that the spelling is correct. 2.9.2.4 Invalid server format You will receive the following error message when the server address supplied was in an invalid format: The supplied server is in an invalid format. Check for any typing mistakes. Verify that there aren't any spelling or typographical errors. 2.9.2.5 Invalid configuration keys present You will receive the following error message when invalid keys are present in a specific section of the serverside web.config file: The '<SECTION>' section of the web.config contains invalid keys: <KEYS>. Ensure no typographical errors were made and that each key is in the appropriate section. In this message, <SECTION> represents the part of the web.config file that contains invalid keys. <KEYS> is a comma-separated list of the offending keys. Ensure that each key is in the correct section, and that no spelling or typographical errors exist. Also, the keys are case-sensitive. 2.9.2.6 Insufficient permissions in IIS You will receive the following error message when the account that the Relativity.Services application pool runs under has insufficient permissions to access a certificate: The account that IIS is running under does not have sufficient permissions to the private key of the certificate referred to by the server-side CertificateFindValue. Via the 'Certificates' snap-in of the Microsoft Management Console, give the appropriate account permissions to the private key. Relativity | Services API Guide - 29 Follow the instructions provided in the error message. See MSDN (http://msdn.microsoft.com/enus/default.aspx) for more information about how to modify private key permissions. 2.9.2.7 Protocol not configured properly You will receive the following error message when a protocol hasn't been properly configured: The protocol '<VALUE>' has not been configured correctly. Ensure that all necessary Windows features are installed, that '<VALUE>' has been added to the list of Enabled Protocols for the IIS application, and the site has a binding for '<VALUE>'. In the error message, <VALUE> indicates the protocol that requires further configuration before it is usable. See Configuring the Services API on page 9. 2.9.2.8 Remote name unreachable You will receive the following error message when the server address supplied can't be reached: The supplied server '<SERVER>' could not be reached. Verify that no spelling or typographical errors exist, and that Windows can resolve the supplied host name. 2.9.2.9 HTTP requested, but SSL is required You will receive the following error message when the server requires SSL, but an attempt was made to communicate using HTTP: A connection attempt was made with HTTP, but the server requires SSL. HTTP must be allowed in IIS if you want to use any of the endpoint configurations that involve HTTP, such as IntegratedHTTP, or UserNamePasswordHTTP. See Configuring the Services API on page 9. 2.9.2.10 SSL trust failure You will receive the following error message when there is an issue establishing an SSL connection: The Certificate Authority of the server's certificate is not trusted by the client. If the server is using a self-signed certificate, install that certificate on the client in the Trusted Root Certification Authorities of the Local Machine if you trust it. The specified server name does not match the SubjectName value of the certificate of the HTTPS binding in IIS. Either use a different certificate on the server, or use a client-side server name that matches against the SubjectName. To resolve this error, ensure the server’s certificate is trusted and that the client is using an appropriate address to reach the server. The address must match the SubjectName of the certificate attached to the HTTPS binding in IIS. See Configuring the Services API on page 9. 2.10 Troubleshooting the Services API Configuration You can use the information in the following sections to troubleshoot the configuration of the Services API. In addition to reviewing error messages, you can also use the Proxy Diagnostic Tool to troubleshoot connections to the Services API. See Testing the Services API configuration on page 25. Relativity | Services API Guide - 30 2.10.1 Logging and debugging settings in the web.config To assist with Services API troubleshooting, enable logging and debugging in the web.config file. (They are disabled by default.) Modify the configuration settings as necessary. 2.10.2 Production environment trace settings To troubleshoot application activity, you can collect trace data from WCF or user-defined trace sources. Trace levels include Warning, Information, and Verbose. Note: Logging may result in application performance degradation. Information and Verbose levels produce greater logging output resulting in a greater performance impact, so they may be best suited for nonproduction environments. This table includes logging configurations recommended for production environments. You will need to update the web.config file with these values. (If you don't anticipate performance degradation, you can set the switchValue attribute to Information to generate additional trace data.) Trace Source WCF User-defined Source Name System.ServiceModel User-defined source name switchValue Attribute Warning Warning, ActivityTracing Additional Attribute propagateActivity="true" Using the recommended production environment configurations, the following sample code defines trace settings for the System.ServiceModel WCF trace source and a user-defined trace source named myUserTraceSource: <system.diagnostics> <sources> <source name="System.ServiceModel" switchValue="Warning" propagateActivity="true" > <listeners> <add name="xml"/> </listeners> </source> <source name="myUserTraceSource" switchValue="Warning, ActivityTracing"> <listeners> <add name="xml"/> </listeners> </source> </sources> <shareListeners> <add name="xml" type="System.Diagnostics.XmlWriterTraceListener" Relativity | Services API Guide - 31 initializeData="C:\logs\Traces.svclog" /> </sharedListeners> </system.diagnostics> 2.10.3 Test environment trace settings In your test environment, make the following updates to the web.config file: n n In the initializeData attribute, set the file name and directory location to the folder where you want to save the log output. Add the following node to the system.servicemodel section: <diagnostics wmiProviderEnabled="true"> <messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" maxMessagesToLog="3000" /> </diagnostics> Use the recommended configuration settings for the test environment listed in the following table. For enhanced logging, add System.ServiceModel.MessageLogging as an additional trace source. (The switchValue attribute doesn’t affect this trace source.) Trace Source WCF Source Name System.ServiceModel User-defined User-defined source name Source Name Information, ActivityTracing or Verbose, ActivityTracing Information, ActivityTracing or Verbose, ActivityTracing Additional Attribute propagateActivity="true" The following sample code defines trace settings for the System.ServiceModel WCF, and the System.ServiceModel.MessageLogging trace sources, as well as a user-defined trace source named myUserTraceSource. The switchValue attribute uses this Information, and ActivityTracing settings. <system.diagnostics> <sources> <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true" > <listeners> <add name="xml"/> </listeners> </source> <source name="System.ServiceModel.MessageLogging"> Relativity | Services API Guide - 32 <listeners> <add name="xml"/> </listeners> </source> <source name="myUserTraceSource" switchValue="Information, ActivityTracing"> <listeners> <add name="xml"/> </listeners> </source> </sources> <sharedListeners> <add name="xml" type="System.Diagnostics.XmlWriterTraceListener" initializeData="C:\logs\Traces.svclog" /> </sharedListeners> </system.diagnostics> 2.10.4 FailureAuditing Configuration Override Setting You can also configure a FailureAuditing override setting on the server hosting the Services API. You can set this value to True if you want to audit failed security events from WCF. Audited security events include transport, message, or negotiate authentication and authorization events. You can view these events in the Windows Event Viewer, since they are written to Windows event log. See Configuration overrides on page 19. 2.10.5 Attempting to access .svc file causes keyset error You receive the following error message when accessing a .svc files that expose an EndpointType configuration requiring a certificate, such as username and password authentication forfor HTTP, HTTPS, or Net.TCP: Keyset does not exist. This error occurs because the permissions on the private key of the certificate are incorrect. It occurs only when the Services API attempts to access a certificate already added to a directory, even though it is in the correct location. You don’t need to relocate the certificate. To resolve this issue, you need to ensure the account running the Relativity.Services application in IIS has access to the private key of the certificate. Complete these steps to set the permissions: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. Log on to the server running the Services API. Click Start. In the Search programs and files box, type mmc. Press Enter to open the Microsoft Management Console. On the File menu, click Add/Remove Snap-in. In the Available snap-ins column, double-click Certificates to open the Certificates snap-in dialog. Select Computer account, and click Next. Select Local computer option for managing snap-ins. Open the Certificates (Local Computer) snap-in. Browse to Personal, and then Certificates. Locate the certificate that you have set for CertificateFindValue property in the web.config file. Relativity | Services API Guide - 33 12. Right-click on the certificate, point to All Tasks, and click Manage Private Keys. 13. To modify the private key permissions, select Add. 14. Grant full permissions to the account running the Relativity.Services IIS application. 2.10.6 Catching EndpointType exceptions You can obtain additional information about EndpointType errors by catching the EndpointTypeCollectionInvalidException exception. This error message is returned when you catch this exception: Error constructing a proxy. Log Info: EndpointType UserNamePasswordNetTCPHTTP Failed: -->Metadata contains a reference that cannot be resolved: 'http://someserver.name.domain/Relativity.Services/SetExecutorNetHTTP.svc?wsd l'. -->The remote name could not be resolved: 'someserver.name.domain' EndpointType UserNamePasswordHTTP Failed: -->Metadata contains a reference that cannot be resolved: 'http://someserver.name.domain/Relativity.Services/SetExecutorHTTP.svc?wsdl'. -->The remote name could not be resolved: 'someserver.name.domain' EndpointType UserNamePasswordNetTCPHTTPS Failed: -->Metadata contains a reference that cannot be resolved: 'https://someserver.name.domain/Relativity.Services/SetExecutorNetHTTPS.svc?ws dl'. -->The remote name could not be resolved: 'someserver.name.domain' EndpointType UserNamePasswordHTTPS Failed: -->Metadata contains a reference that cannot be resolved: 'https://someserver.name.domain/Relativity.Services/SetExecutor.svc?wsdl'. -->The remote name could not be resolved: 'someserver.name.domain' 2.10.7 Connection errors in load-balanced systems If you have configured your environment for load balancing and HTTPS, you may receive errors if the API connects to a different URL than the WSDL. To resolve these errors, set the binding in IIS to a certificate from an internal CA authority or to a wildcard certificate. 2.10.8 Could not load type 'System.ServiceModel.Activation.HttpModule' This error is displayed if you have installed .NET 4.0 features after installing .NET 4.5: Server Error in '/Relativity.Services' Application. Could not load type 'System.ServiceModel.Activation.HttpModule' from assembly 'System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Open a Visual Studio 64-bit console. In Admin mode, execute this command: aspnet_regiis.exe /iru Relativity | Services API Guide - 34 The program, aspnet_regiis.exe, is located in one of the following directories by default. The minor version number in the directory path may vary, such as .30319 in this example. %windir%\Microsoft.NET\Framework64\v4.0.30319 %windir%\Microsoft.NET\Framework\v4.0.30319 If the previous command didn't resolve the issue, execute this command: servicemodelreg -r For more information, see The DCS ServiceModelReg Utility on the Microsoft Developer Network site. 2.10.9 Erroneous calls to internal web server in load-balanced systems When you use the client proxy to access the Services API, a second erroneous call is made to an internal web server. The URIs in the WSDL document points to inaccessible internal websites. Microsoft has identified this behavior as a known issue exhibited by the WCF service in load-balanced environments. The following environmental configurations are associated with this issue: n n n Windows Communication Foundation (WCF) service in a load-balanced environment Microsoft .NET 3.0 and 3.5 Windows Server 2003, Windows XP, Windows Vista, and Windows 2008 To resolve this issue, specify the following service behavior by adding these settings to the web.config file. Replace <name> with the behavior name of the WCF service. <serviceBehaviors> <behavior name="<name>"> <useRequestHeadersForMetadataAddress> <defaultPorts> <add scheme="http" port="81" /> <add scheme="https" port="444" /> </defaultPorts> </useRequestHeadersForMetadataAddress> </behavior> </serviceBehaviors> For more information, see this Microsoft article (http://support.microsoft.com/kb/971842). 2.10.10 HTTP error 404.17 or 404.3 When you attempt to reach one of the service endpoints from a server hosting the Relativity.Services, you receive an HTTP 404.17 or 404.3 errors. Relativity | Services API Guide - 35 Use the following steps to install WCF on machines hosting the Relativity.Services: 1. Open a command prompt on a machine hosting the Relativity.Services. 2. Change to one of the following directories depending on your machine: 64-bit:cd C:\Windows\Microsoft.NET\Framework64\v4.0.30319 32-bit:cd C:\Windows\Microsoft.NET\Framework\v4.0.30319 3. Press Enter. 4. Type ServiceModelReg -I. Press Enter. 5. Repeat steps 1 - 4 on each machine that hosts the Relativity.Services in your environment. n n Note: For more information, see http://iweb.adefwebserver.com/Default.aspx?tabid=57&EntryID=34. 2.10.11 Incorrect version of .NET enabled for application pools Since Relativity 7.0 or above only supports .NET Framework v 4.0, you may need to register the .NET version for your environment. Complete these steps: 1. Use the following commands to register the appropriate version of .NET (in Framework64): aspnet_regiis -iru -enable 2. Change the application pool to the appropriate version of .NET and recycle the application pool. Note: If the application pool fails to restart, restart IIS and then start the application pool again. 2.10.12 Missing handler mappings Error messages don't provide detailed information for troubleshooting this issue. You will need to compare the handler mappings on the Services API server to those of a known working server. For reference, the Relativity | Services API Guide - 36 following path is used for the handler mapping: %SystemRoot%\Microsoft.NET\<framework>\v2.0.50727\aspnet_isapi.dll The <framework> represents Framework or Framework64 directory. Note: The API on Relativity 7 includes handler mappings for .NET 4.0 as well as those for 2.0, using the v4.0.30319 directory. As illustrated below, these handler mappings may be missing: n svc-ISAPI-2.0 Relativity | Services API Guide - 37 n svc-ISAPI-2.0-64 2.10.13 Pipe endpoint could not be found You will receive the following error message when Net.Pipe is not configured: System.IO.PipeException: The pipe endpoint 'net.pipe://myserver.kcura.corp/Relativity.Services/AuthenticationNetHTTP.svc' could not be found on your local machine. This error indicates that no endpoint was listening at Net.Pipe, which can be caused by an incorrect address or SOAP action. You may see this error in the Relativity error log depending on where the call to the Services API was made. To resolve this error, see Configuring Net.Pipe or Net.TCP connectivity on page 16. 2.10.14 System.ServiceModel.ExceptionDetail Error You receive the following error message when using the Services API: Type 'System.ServiceModel.ExceptionDetail' in Assembly 'System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable. For example, you may see this error message when communicating across application domains. If the exception thrown from one domain to another isn't serializable, this error occurs. 2.10.15 Unable to access .svc file path in browser After you install the Windows Non-HTTP Activation feature, you receive the following error when attempting to access a .svc file path in the web browser: Could not load type ‘System.ServiceModel.Activation.HttpModule’ from assembly ‘System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089’ To resolve this issue, execute the following command line statement: Relativity | Services API Guide - 38 aspnet_regiis.exe /iru 2.10.16 Unable to access website You will be unable to access your website if the HTTP or HTTPS host header references your local machine name. You can update the HTTP or HTTPS host header and reset the ISS to resolve this issue. Note: Reset IIS during off-hours. 2.10.17 Updating the HTTP host header If your scheme is HTTP, use the following steps to update the host header: 1. 2. 3. 4. 5. 6. 7. 8. 9. On the web server, log on as a member of the Administers group. Open IIS Manager. Under the Sites node, select your website. In the Actions pane, click Bindings to display the Site Bindings. Select HTTP, and click Edit. Update the Host Header on the dialog. Reset IIS. Try to connect. Repeat step 1 -9 on all servers that host Relativity.Services. 3 Upgrading the Services API Upgrading the Services API may require specific changes to your source code as well as updates to your server and client-side configurations. As part of the upgrade process, you may want to perform endpoint configuration tests in order to ensure clients are successfully communicating with the Services API. See Testing the Services API configuration on page 25. 3.1 Upgrading from Relativity 8 kCura initially introduced the new RSAPIClient proxy and related function in the Relativity 8.0.291.1 patch release - September 13, 2013. The following upgrade guidelines included information from this patch release due to the impact of these enhancements on development with the Services API. 3.1.1 Upgrade guidelines for the Relativity 8.1 To upgrade to Relativity 8.1, use these guidelines if your environment runs Relativity 8.0.291.1 or above. 3.1.1.1 Update .dll references in your projects To upgrade your code, run the Relativity SDK installer to obtain the new kCura.Relativity.Client.dll, Relativity.API.dll, and other .dll files. Add references to the new .dlls to your projects. On the Relativity8.1 Developers site, obtain a copy of the Relativity SDK Installer from the Development environment guidelines page. Relativity | Services API Guide - 39 3.1.1.2 Upgrade your development environment to .NET 4.5 Use Microsoft .NET 4.5 framework for new development. When you update any of your current applications, you should also target the .NET 4.5 framework. Note: We recommend testing your existing applications for compatibility with .NET 4.5. Depending on the results of your testing, you may need to recompile your applications for optimum performance. 3.1.2 Upgrade guidelines from the Relativity 8.0.291.1 patch release To upgrade to Relativity 8.1, use these guidelines if your environment runs Relativity 8.0.275.3 or below. In addition, see Upgrade guidelines for the Relativity 8.1 on the previous page. 3.1.2.1 (Optional) Update references to WCF Web Service File (.svc) files The WCF Web Service files (.svc) have been renamed to coincide with changes made for the retrieval of metadata over the same protocol as that used for the service connection. If you aren't using the kCura.Relativity.Client.dll for your client development, you may need to update your code to accommodate these changes. Changes to .svc file names for the Authentication service The following table lists changes to the file names for the Authentication service by EndpointType enum. A blank entry indicates that the EndpointType enum didn't exist in previous versions. EndpointType enums IntegratedHTTP IntegratedHTTPS IntegratedNetNamedPipe IntegratedNetNamedPipeViaHTTP IntegratedNetNamedPipeViaHTTPS IntegratedNetTCP IntegratedNetTCPViaHTTP IntegratedNetTCPViaHTTPS UserNamePasswordHTTP UserNamePasswordHTTPS UserNamePasswordNetNamedPipe UserNamePasswordNetTCP UserNamePasswordNetTCPViaHTTP UserNamePasswordNetTCPViaHTTPS Relativity | Services API Guide - 40 Authentication service (.svc) in Relativity 8 AuthenticationWinAuthHTTP.svc AuthenticationWinAuth.svc AuthenticationNetWinAuth.svc AuthenticationNetWinAuth.svc AuthenticationNetWinAuth.svc AuthenticationNetTCPWinAuth.svc AuthenticationNetTCPWinAuth.svc AuthenticationNetTCPWinAuth.svc AuthenticationForms.svc Authentication.svc AuthenticationNetForms.svc AuthenticationNetTCPForms.svc AuthenticationNetTCPForms.svc i AuthenticationNetTCPForms.svc Authentication service (.svc) prior to Relativity 8 No change No change AuthenticationNetHTTP.svc AuthenticationNetHTTPS.svc AuthenticationNetHTTP.svc AuthenticationNetHTTPS.svc AuthenticationHTTP.svc No change AuthenticationHTTP.svc AuthenticationNetHTTPSForms.svc 3.1.2.2 Use of State property on ServiceInformation class The RSAPIClient class doesn't include a State property that was previously available on the ArtifactManagerProxy class. In Relativity 8.1, the ArtifactManagerProxy returns only Open for the State property on ServiceInformation object. 3.1.2.3 Use updated EndpointType enums The UserNamePasswordNetNamedPipe endpoint type replaces deprecated endpoint types listed in the following table. This table also includes mappings between other endpoint types deprecated during the Relativity 8 release and their new replacements. For more information, see Upgrading the Services API in the Relativity 8 Developers site. Note: As a best practice, use the new endpoint types in all future applications that you develop. kCura currently supports the deprecated endpoint types only for backwards compatibility. Deprecated EndpointType enums IntegratedNetNamedPipeViaHTTP IntegratedNetNamedPipeViaHTTPS IntegratedNetTCPViaHTTP IntegratedNetTCPViaHTTPS UserNamePasswordNetTCPViaHTTP UserNamePasswordNetTCPViaHTTPS (Deprecated in Relativity 8.0.291.1) UserNamePasswordNetNamedPipeViaHTTP UserNamePasswordNetNamedPipeViaHTTPS New EndpointType enums IntegratedNetNamedPipe IntegratedNetTCP UserNamePasswordNetTCP UserNamePasswordNetNamedPipe 3.1.2.4 Client-side configuration overrides automatically set With the introduction of the new RSAPIClient proxy, Relativity automatically sets most client-side configuration override values available on the ProxySettings class. It copies these settings from your server to your client-side configuration file. These deprecated properties on the ProxySettings class are now copied from the server configuration: n n n n n n n n MaxArrayLength MaxBufferSize MaxBufferPoolSize MaxDepth MaxReceivedMessageSize MaxStringContentLength SendTimeout MaxConnectionRetries You can optionally set the CertificateFindValue and CertificateValidation properties on the client-side as in previous Relativity releases. See Setting client configuration overrides on page 19. 3.2 Upgrading from Relativity 7.5 Your upgrade path for Relativity 8.1 depends on the patch version currently installed in your environment: Relativity | Services API Guide - 41 n n Relativity Patch 7.5.632.83 or above - review the information provided under Upgrading from Relativity 8 on page 39. You may have already completed some of these tasks as you upgraded to the latest patches. Relativity 7.5.632.72 or below - review Relativity Patch 7.5.632.83 Guidelines on the Relativity 7.5 Developers site and the information provided under Upgrading from Relativity 8 on page 39. 3.3 Upgrading from Relativity 7.4 and earlier To upgrade to Relativity 8.1, make any required updates to your existing code and configuration settings. Review the following information that describes feature changes and enhancements: n n Upgrading the Services API and Relativity Patch 7.5.632.83 Guidelines on the Relativity 7.5 Developers site Upgrading from Relativity 8 on page 39 4 Getting started with the Services API The Services API provides you with a set of web services for programmatically manipulating many of the object types available in Relativity. Depending on the object type, you have the option to create, read, update, delete, and query on it. You can use the functionality provided by the Services API to perform many of the same tasks that you can complete through a Relativity web UI. 4.1 Prerequisites for a development environment Complete the following tasks before you set up your development environment: n (Optional) Complete the configuration steps for the Services API only if you want to use HTTP, HTTPS, or TCP as the scheme, or customize the client and server configuration settings. See Configuring the Services API on page 9 Note: You don't need to complete any configuration steps if you use Net.Pipe as installed on the IIS when you run the Relativity installer. n n Confirm that you have .NET version 4.5 installed on your development machine. On the Relativity 8.1 Developers site, obtain a copy of the Relativity SDK Installer, and see the Development environment guidelines. 4.2 Services API materials available in the SDK Installer When you run the 32 or 64-bit version of the Relativity SDK Installer, its add a folder containing Services API materials in one of the following default location: n n 64-bit version - installs in ...\Program Files\kCura Corporation\Relativity SDK\RSAPI\ 32-bit version - installs in ...\Program Files (x86)\kCura Corporation\Relativity SDK\RSAPI The RSAPI folder has the following subfolders with their related contents: Relativity | Services API Guide - 42 n n n n Client - contains the kCura.Relativity.Client.dll, which must be reference by a Visual Studio project used for development with the Services API. Documentation - contains the Relativity - Services API.chm file with the Services API class library. Samples - includes various subfolders with sample code. In the kCura.Relativity.Client.APISamples folder, you will find a project containing sample class files in both VB.NET and C#. Tools - includes the Relativity Services API Diagnostic Tool and the self-signed certificate utility. See Relativity Services API Diagnostic Tool on page 25 and Creating a self-signed certificate on page 12. 4.3 Setting up a project in Visual Studio 1. To run the installer, double-click the kCura.Relativity.SDK.Setup.msi, and follow the instructions in the installation wizard. 2. Create a new project in Visual Studio. (The following steps use Visual Studio 2012 as an example.) 3. Open the Solution Explorer. 4. Confirm that the Target framework is .NET Framework 4.5. (In the Solution Explorer, expand your project and right-click Properties. Click Open to display the Application tab in the left pane. Select .NET Framework 4.5 in the Target framework box.) Relativity | Services API Guide - 43 5. Add a reference to the kCura.Relativity.Client.dll. (In the Solution Explorer, right-click References, and then click Add References to display the Reference Manager dialog. Click the Browse button and select the kCura.Relativity.Client.dll.) You can find this .dll in a Relativity web server installation, or in the RSAPI folder of the SDK installation directory. The folder is installed at one of these locations by default: n n 64-bit version - installs in ...\Program Files\kCura Corporation\Relativity SDK\RSAPI\Client 32-bit version - installs in ...\Program Files (x86)\kCura Corporation\Relativity SDK\RSAPI\Client 6. Add references to the following .NET framework assemblies: System.Runtime.Serialization, and System.ServiceModel. (In the Reference Manager dialog, expand Assemblies and click Framework.) Relativity | Services API Guide - 44 7. (Optional) Point the RelativityServices API Diagnostic Tool at your server to determine the endpoint configurations that are exposed. See Testing the Services API configuration on page 25. 4.4 Creating a simple program with the Services API This tutorial helps you to create a simple program that uses the Services API to perform CRUD and query operations. It illustrates how to extend the functionality provide by a sample application developed using the Application Deployment System. The tutorial uses a Custodian object to demonstrate how to create Relativity Dynamic Objects (RDOs) using a typed class in the kCura.Relativity.Client.DTOs namespace. The Custodian class represents a person and its Fields store data such as a phone number and first and last name. 4.4.1 Before you begin To run this sample program, complete the following tasks to set up your development environment: n n n Confirm that you have the required software. See Prerequisites for a development environment on page 42. Create a workspace in your target Relativity instance. You don't need to add documents to the workspace. Confirm that you have these permissions: Relativity Administrator rights to log in to Relativity through Services API Relativity Script Administrator rights to install an application Download RSAPIGettingStartedTutorial.zip file on the Relativity 8.1 Developers site. This file contains the application and source code for this sample program. Unzip the Services API Tutorial Application, and install the RA_Tutorial_20130711213312.xml file to your workspace. For more information, see Installing an application on the Relativity 8.1 Developers site. Create a C# console application in Visual Studio. See Setting up a project in Visual Studio on page 43. o o n n n Relativity | Services API Guide - 45 Note: The code for this program references several constants that represent GUID and string values. The TutorialConstants class provides a complete list of these constants. To view this class, open the Program.cs file included in the RSAPIGettingStartedTutorial.zip file. 4.4.2 Adding directives for required namespaces To your applications, add using directives referencing the namespaces that contain the classes used to create a proxy and work with DTOs. If these Services API namespaces aren't available in your application, see Getting started with the Services API on page 42. using using using using using System; System.Collections.Generic; System.Linq; kCura.Relativity.Client; DTOs = kCura.Relativity.Client.DTOs; 4.4.3 Adding the Main method The sample program has a Main() method that includes code for calls to subsequent methods for creating the proxy, querying for Workspaces, creating an RDO, and performing other tasks. public static void Main(string[] args) { // Create a client which uses Windows Authentication // to log in to the Relativity Services on the local machine. // Set up the current Windows user as a Relativity System // Administrator for the purposes of this tutorial. using (IRSAPIClient client = CreateClient()) { // Query for a workspace and access it. // Change the following string to the name of a workspace in your copy of Relativity. FindAndEnterWorkspace(client, TutorialConstants.TARGET_WORKSPACE_NAME); // Create a Custodian RDO in the context of the workspace. Int32 rdoID = CreateRdo(client); // Create an Address Field on the Custodian. Int32 fieldID = CreateField(client); // Update the RDO with new data. UpdateRdo(client, rdoID, fieldID); // Read the updated RDO. Relativity | Services API Guide - 46 ReadRdo(client, rdoID); // Query for the ArtifactIDs of all Custodians. QueryRdo(client); // Delete the newly added Field from the Custodian RDO. DeleteField(client, fieldID); // Delete the newly created RDO. DeleteRdo(client, rdoID); // Log out is handled by the RSAPIClient object. } // Exiting the using block calls the Dispose() method on the RSAPIClient, which ensures the client is cleaned up. PauseBeforeExit(); } 4.4.4 Creating the RSAPIClient proxy You must be authenticated before you can manipulate objects in Relativity through the Services API. You need to create an instance of the RSAPIClient class to hold the session data. The overloaded constructor for the class provides you with the ability to choose the combination of endpoint type, authentication method, and optional configuration settings appropriate for your application development goals. The proxy automatically logs in to Relativity using the specified AuthenticationType so you don't need to call a login method. See Creating the RSAPIClient proxy on page 61. This code sample illustrates how to instantiate the proxy with a constructor that uses integrated Windows authentication. public static IRSAPIClient CreateClient() { // Create a new instance of RSAPIClient. The first parameter indicates the endpoint Uri, // which indicates the scheme to use. The second parameter indicates the // authentication type. The RSAPIClient members page in the Services API class library // documents other possible constructors. The constructor also ensures a logged in session. string localHostFQDN = System.Net.Dns.GetHostEntry("localhost").HostName; Uri endpointUri = new Uri(string.Format("https://{0}/relativity.services", localHostFQDN)); IRSAPIClient rsapiClient = new RSAPIClient(endpointUri, new IntegratedAuthCredentials()); Console.WriteLine("\tClient created and logged in."); Relativity | Services API Guide - 47 return rsapiClient; } 4.4.5 Querying for a Workspace Each instance of the RSAPIClient class has an APIOptions property. Before you can use a workspace, you must set the WorkspaceID property on APIOptions to its ArtifactID. You can perform a query on the name of the workspace to return its ArtifactID. The following code sample illustrates how to use the Query DTO with a TextCondition to search for a Workspace by name and then sets its ArtifactID on the APIOptions. This code uses a Like text condition, but you could also an EqualTo condition. Both are available in the TextConditionEnum enumeration. Since Workspace names aren't required to be unique, the code sample calls the Any() method on the Results object. This Workspace provides the context for other operations. private static void FindAndEnterWorkspace(IRSAPIClient client, string workspaceName) { Console.WriteLine("\n\tFinding and entering Workspace..."); // Use the TextCondition to match workspaces with names similar to the specified string. var workspaceCondition = new TextCondition (DTOs.ArtifactFieldNames.TextIdentifier, TextConditionEnum.Like, workspaceName); // Build a query with the workspaceCondition. var query = new DTOs.Query<DTOs.Workspace> { Condition = workspaceCondition }; query.Fields = DTOs.FieldValue.NoFields; // Send the query and receive results. DTOs.QueryResultSet<DTOs.Workspace> results = client.Repositories.Workspace.Query(query); if (!results.Success) { WriteFailedResultAndExit(results, client); } if (!results.Results.Any()) { // No Workspace with the specified name exists. Relativity | Services API Guide - 48 WriteErrorAndExit("No Workspace matching condition found.", client); } // To begin using this workspace, set the WorkspaceID of your instance of APIOptions to the // ArtifactID of the Workspace. client.APIOptions.WorkspaceID = results.Results.First().Artifact.ArtifactID; } 4.4.6 Creating a Relativity Dynamic Object DTO To develop a custom application, you can create RDOs and other objects using the typed classes in the kCura.Relativity.Client.DTOs namespace. The classes in this namespace are called DTOs. See Data Transfer Objects (DTOs) on page 67. This sample code shows you how to create Custodian RDO by completing the following steps that are also common to other DTOs: n n n n Set all required properties for a new DTO. Call the Create() method. Confirm that the object was created successfully. Save or return the ArtifactID for future use. private static Int32 CreateRdo(IRSAPIClient client) { Console.WriteLine("\n\tCreating RDO... "); var rdo = new DTOs.RDO(); rdo.TextIdentifier = DateTime.Now.Ticks.ToString(); rdo.Fields.Add(new DTOs.FieldValue(TutorialConstants.FIRST_NAME_FIELD_GUID, TutorialConstants.CUSTODIAN_FIRST_NAME_VALUE)); rdo.Fields.Add(new DTOs.FieldValue(TutorialConstants.LAST_NAME_FIELD_GUID, TutorialConstants.CUSTODIAN_LAST_NAME_VALUE)); rdo.Fields.Add(new DTOs.FieldValue(TutorialConstants.PHONE_NUMBER_FIELD_ GUID, TutorialConstants.CUSTODIAN_PHONE_NUMBER_VALUE)); // Set the ArtifactTypeName, ArtifactTypeID, or ArtifactTypeGuids for any new RDO. rdo.ArtifactTypeGuids = new List<Guid>() { TutorialConstants.CUSTODIAN_ TABLE_GUID }; DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Create(rdo); // Check for success of Create() method. if (!results.Success) { Relativity | Services API Guide - 49 WriteFailedResultAndExit(results, client); } if (!results.Results.Any()) { WriteErrorAndExit("FAILURE: RDO creation succeeded but returned no Artifacts.", client); } // Get the ArtifactID of the new Custodian. Int32 rdoID = results.Results.Single().Artifact.ArtifactID; Console.WriteLine("\tArtifactID of new RDO: {0}", rdoID); return rdoID; } 4.4.7 Creating a Field on a RDO You can create Fields on DTOs to store metadata and other information about them. To the Custodian RDO, you add a Field with a name, which includes the word Address followed by the count of CPU ticks since 12:00:00 midnight on Jan 1, 2001. This process ensures that Field name is unique, such as Address 635133920940413625. For more information, see Field on page 113. private static Int32 CreateField(IRSAPIClient client) { Console.WriteLine("\n\tCreating Field... "); DTOs.Field field = new DTOs.Field(); field.ObjectType = new DTOs.ObjectType(TutorialConstants.CUSTODIAN_TABLE_ GUID); field.Name = string.Format("Address {0}", DateTime.Now.Ticks); field.FieldTypeID = FieldType.FixedLengthText; field.Length = 255; field.IsRequired = false; field.IncludeInTextIndex = false; field.Unicode = true; field.AllowHTML = false; field.OpenToAssociations = false; field.Linked = false; field.AllowSortTally = true; field.Wrapping = true; field.AllowGroupBy = false; field.AllowPivot = false; field.IgnoreWarnings = true; field.Width = "10"; Relativity | Services API Guide - 50 DTOs.WriteResultSet<DTOs.Field> results = client.Repositories.Field.Create (field); if (!results.Success) { WriteFailedResultAndExit(results, client); } if (!results.Results.Any()) { WriteErrorAndExit("FAILURE: Field creation succeeded but returned no Artifacts.", client); } Int32 fieldID = results.Results.Single().Artifact.ArtifactID; Console.WriteLine("\tArtifactID of new Field: {0}", fieldID); return fieldID; } 4.4.8 Updating Fields on an RDO You can update specific Fields on a DTO. This code sample creates a new DTO with the same ArtifactID as the one that you want to update. It illustrates how to update value of the Last Name and Text Identifier fields, and sets the value for the newly created Address field, which is currently blank. The ArtifactTypeGuids property indicates the object type of the RDO, which is Custodian. You can use ArtifactGuid instead of ArtifactID, and ArtifactTypeID or ArtifactTypeName instead of ArtifactTypeGuids. We recommend using GUIDs since they are unique across Relativity and they can't be modified. See GUIDs in application development on page 78. This code sample updates the Last Name and Text Identifier fields on the Custodian RDO. It also sets the Address, which is blank. private static void UpdateRdo(IRSAPIClient client, Int32 rdoID, Int32 fieldID) { Console.WriteLine("\n\tUpdating RDO... "); // rdoID is the ArtifactID of the RDO that you want to update. // The ArtifactTypeGuids indicates object type of the RDO, which is Custodian. // You can use an ArtifactGuid instead of the ArtifactID. var updatedRdo = new DTOs.RDO(rdoID); // You can replace the ArtifactTypeGuids with either ArtifactTypeID or ArtifactTypeName. // Use GUIDs because they are unique across all of Relativity and can't be modified. Relativity | Services API Guide - 51 updatedRdo.ArtifactTypeGuids = new List<Guid>() { TutorialConstants.CUSTODIAN_TABLE_GUID }; // List all Fields to be updated and their new values. // Any Fields not listed remain unchanged. updatedRdo.Fields = new List<DTOs.FieldValue>(); updatedRdo.Fields.Add(new DTOs.FieldValue(TutorialConstants.LAST_NAME_FIELD_ GUID, TutorialConstants.UPDATED_CUSTODIAN_LAST_NAME_VALUE)); updatedRdo.Fields.Add(new DTOs.FieldValue(TutorialConstants.TEXT_IDENTIFIER_ FIELD_GUID, TutorialConstants.UPDATED_CUSTODIAN_TEXT_IDENTIFIER_VALUE)); updatedRdo.Fields.Add(new DTOs.FieldValue(fieldID, TutorialConstants.UPDATED_CUSTODIAN_ADDRESS_VALUE, true)); // Send updated RDO and receive results. DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Update (updatedRdo); if (!results.Success) { WriteFailedResultAndExit(results, client); } Console.WriteLine("\tLast Name, Text Identifier, and Address [...] values updated."); } 4.4.9 Reading an RDO To read, call the Read() method which takes a DTO of type RDO as an argument. The following code sample reads the ArtifactTypeName of the Custodian RDO. private static void ReadRdo(IRSAPIClient client, Int32 rdoID) { Console.WriteLine("\n\tReading RDO... "); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Read(new DTOs.RDO(TutorialConstants.CUSTODIAN_ TABLE_GUID, rdoID)); if (!results.Success) { WriteFailedResultAndExit(results, client); } if (!results.Results.Any()) { Relativity | Services API Guide - 52 WriteErrorAndExit("FAILURE: RDO read succeeded but returned no Artifacts.", client); } // The following line prints "ArtifactTypeName of RDO is 'Custodian'." Console.WriteLine("\tArtifactTypeName of RDO is '{0}'.", results.Results.Single().Artifact.ArtifactTypeName); } 4.4.10 Querying for RDOs You can perform searches for RDOs by using the Query DTO. In addition, you can return specific Field values on an Artifact by adding them to the query: query.Fields.Add(new DTOs.FieldValue("Phone Number")); You can use this sample code to query for all Custodians and then print their ArtifactIDs. If your workspace contains only the newly added Custodian, then this code prints just its ArtifactID. For information about available fields, see Constant Field names on page 146. private static void QueryRdo(IRSAPIClient client) { Console.WriteLine("\n\tQuerying RDO... "); var rdoQuery = new DTOs.Query<DTOs.RDO>(); rdoQuery.ArtifactTypeGuid = TutorialConstants.CUSTODIAN_TABLE_GUID; rdoQuery.Fields = DTOs.FieldValue.NoFields; // To obtain more details about an Artifact, add Fields to the query. // For example, you might add: query.Fields.Add(new DTOs.FieldValue("Phone Number")); // Send the Query to Relativity. DTOs.QueryResultSet<DTOs.RDO> results = client.Repositories.RDO.Query (rdoQuery); if (!results.Success) { WriteFailedResultAndExit(results, client); } if (!results.Results.Any()) { WriteErrorAndExit("FAILURE: RDO query succeeded but returned no Artifacts.", client); Relativity | Services API Guide - 53 } Console.WriteLine("\tArtifactID of each Custodian:"); foreach (var res in results.Results) { Console.WriteLine("\t {0}", res.Artifact.ArtifactID); } } 4.4.11 Deleting a Field on an RDO You can remove a Field from an RDO by calling the Delete() method and passing the ArtifactID of the Field to it. To confirm the deletion, this code sample attempts to read the Field. private static void DeleteField(IRSAPIClient client, Int32 fieldID) { Console.WriteLine("\n\tDeleting Field... "); // Delete the Custodian RDO's newly added Field. DTOs.WriteResultSet<DTOs.Field> deleteResult = client.Repositories.Field.Delete(new DTOs.Field(fieldID)); // Try to read the same Field. DTOs.ResultSet<DTOs.Field> readResult = client.Repositories.Field.Read(new DTOs.Field(fieldID)); if (!deleteResult.Success) { WriteFailedResultAndExit(deleteResult, client); } else if (readResult.Success) { WriteErrorAndExit("FAILURE: Reading back the deleted Field should not have succeeded.", client); } } 4.4.12 Deleting an RDO You remove a DTO from Relativity by calling the Delete() method and passing the ArtifactID of the object to it. This code sample deletes the Custodian RDO and then attempts to perform a Read operation to confirm that this object no longer exists. private static void DeleteRdo(IRSAPIClient client, Int32 rdoID) { Relativity | Services API Guide - 54 Console.WriteLine("\n\tDeleting RDO... "); // Delete the Custodian with ArtifactID of rdoID. DTOs.WriteResultSet<DTOs.RDO> deleteResult = client.Repositories.RDO.Delete(new DTOs.RDO(TutorialConstants.CUSTODIAN_ TABLE_GUID, rdoID)); // Try to read the same Custodian. DTOs.ResultSet<DTOs.RDO> readResult = client.Repositories.RDO.Read(new DTOs.RDO(TutorialConstants.CUSTODIAN_ TABLE_GUID, rdoID)); if (!deleteResult.Success) { WriteFailedResultAndExit(deleteResult, client); } else if (readResult.Success) { WriteErrorAndExit("FAILURE: Reading back the deleted Custodian should not have succeeded.", client); } } 4.4.13 Exiting the program When you finish a session, you don't need to complete any additional steps to log out or close the proxy. The RSAPIClient automatically completes these tasks. This sample code requests a response from the user before the program closes. public static void PauseBeforeExit() { Console.WriteLine("\nPlease press enter to end the program."); Console.ReadLine(); } 4.4.14 Writing success and failure messages The following sample code illustrates how to write out messages that indicate the success or failure of the program, as well as how to log out if an error occurs. 4.4.14.1 Obtaining a result message This sample code attempts to find the first Result in ResultSets with a non-empty, non-whitespace message. The individual Results usually contain a message indicating when a failure occurs. However, this code shows the overall message for the ResultSet when a more specific failure message isn’t available. This code can be used to read the most specific messaging from any ResultSet. However, this program only attempts to read Relativity | Services API Guide - 55 messaging from failed operations, because successful operations rarely contain messaging. See Obtaining an error message below. private static string GetResultMostSpecificResultSetMessage<T> (DTOs.ResultSet<T> results) where T : DTOs.Artifact { string resultMessage = results.Results.Any(result => !string.IsNullOrWhiteSpace(result.Message)) ? results.Results.First().Message : results.Message; return resultMessage; } 4.4.14.2 Stopping the program due to an error This sample code writes out a message and then logs out of the proxy when an error occurs. private static void WriteErrorAndExit(String message, IRSAPIClient client) { Console.WriteLine(message); PauseBeforeExit(); Environment.Exit(1); } 4.4.14.3 Obtaining an error message This sample code illustrates how to obtain an error message from a ResultSet object, and how to write out the message before terminating the program. For more information about this code, see Obtaining a result message on the previous page. private static void WriteFailedResultAndExit<T>(DTOs.ResultSet<T> results, IRSAPIClient client) where T : DTOs.Artifact { string failureMessage = GetResultMostSpecificResultSetMessage(results); WriteErrorAndExit(string.Format("FAILURE: {0}", failureMessage), client); } 4.4.15 Building and running the program You can download the complete source code for building and running the client in Visual Studio. Relativity | Services API Guide - 56 Note: You need to install the application as well as update the username and password so the sample program can run in your Relativity environment. See Before you begin on page 45. After running your program, you should see results similar to that displayed here, but with different ArtifactIDs: Proxy created. Logged in. Finding and entering Workspace. Creating RDO... ArtifactID of new RDO: 1042640 Creating Field... ArtifactID of new Field: 1042641 Updating RDO... Last Name, Text Identifier, and Address […] values updated. Reading RDO... ArtifactTypeName of RDO is 'Custodian' Querying RD... ArtifactID of each Custodian: 1042640 Deleting Field… Deleting RDO… Please press enter to end the program. 5 Basic Services API concepts Learn about the Services API features, architecture, deployment process, classes and methods by reviewing these key concepts. 5.1 Services API features The Services API enables you to write highly customized solutions on top of Relativity by providing functionality to perform these development tasks: n n n n Create Dynamic Objects for use in Relativity. Create custom web pages with unique layouts that dynamically display information stored in a Relativity database. Develop event handlers that run on pre-save, post-save, console, and other events within Relativity. Integrate Relativity with external applications to extract, update, or add data. Relativity | Services API Guide - 57 n n n Perform mass operations such as editing, creating, or deleting a group of Relativity objects in a single call. Create Relativity objects using DTOs, which ensure type safety and minimize casting errors. Create custom agents to perform background processing and long-running operations. 5.2 Services API and the Relativity Platform The Services API is a major component of the Relativity platform, which also includes event handlers, custom pages, and agents. It facilitates communication between each of these components and the Relativity engine (or business layer) by providing the proxy class necessary to interact with it. When a user performs a task, a request is sent to the Services API, which passes these calls to the Relativity engine. The code in this layer contains the logic that controls processing of data and access to the database. The following diagram illustrates how the Service API is used by these components of the Relativity platform. You can leverage the functionality provide by the Services API when you develop custom code for each of these key components: n n Custom pages - You can use custom pages to create a unique user interface for interactions with Relativity through the Services API or direct database connections. With the Services API, you can perform validation and exception handling to control the interactions between your custom pages and the Relativity engine. These programming options aren’t available through direct database connections. When you create a custom page, reference the kCura.Relativity.Client.dll, which is the assembly used to obtain a proxy by instantiating the RSAPIClient class. Event Handlers - You can develop new event handlers or modify existing ones to provide custom functionality. They are called only in the Relativity web UI and they perform operations based on user actions. Event handlers communicate with Relativity through the Services API, the ActiveArtifact class, or direct database connections. To use the Services API, you can reference the kCura.Relativity.Client.dll in your event handler code, and use API Helpers to create an instance of the Relativity | Services API Guide - 58 n RSAPIClient class, which provides methods on the proxy and exception handling. For more information see Getting Started with the Relativity API Helpers on the Relativity 8.1 Developers site. Agents - You can also use agents to communicate with the Relativity engine through an instance of the Services API. Agents are background processes that run jobs performing processing, indexing, or other custom tasks. Many Relativity environments use multiple agent servers to run these background jobs. On each server, the Windows Service is used to deploy a copy of the Services API rather than using IIS. Since the agent server is self-hosting an instance of the Services API, the address of the proxy is always localhost. An advantage of this configuration is reduced network traffic since the agents can use the proxy on their local machine rather than send requests to a web server hosting it. It also eliminates the need to install the IIS on agent servers. 5.3 Client Proxy In the Services API, the client-side proxy enables applications to send and receive messages over a variety of transport protocols. This proxy is created by instantiating the RSAPIClient class that lives in the kCura.Relativity.client.dll. To use the Services API for custom development, you need to write .NET code and reference this assembly in your project. You also have to configure endpoints that this proxy uses to communicate with the Services API. When you call an operation on the RSAPIClient, your request is sent to the Services API. 5.3.1 Backwards Compatibility kCura strives to maintain backwards compatibility with each release of the Services API. Compatibility is guaranteed at the .NET client level, which means that the public methods and classes of the client usually don’t change in a breaking way. In contrast, the web methods exposed at the server do change between releases. By using the RSAPIClient proxy, you are shielded from back-end changes made to the Services API. If breaking changes are introduced to the client library, the Services API release notes clearly documents them. Because the web methods used by the RSAPIClient can change between releases, the Services API doesn't support the development of custom proxies that directly communicate with the WSDL definitions provided by Relativity.Services. You shouldn't attempt to create your own .NET proxy based on the WSDLs provided by those services. Relativity | Services API Guide - 59 5.4 Deploying the Services API You deploy the Services API as a web-based service hosted hosted in Internet Information Services (IIS).You can configure your environment with all available endpoints, including HTTPS, HTTP, Net.TCP, and Net.Pipe. The Services API supports username/password and Windows authentication methods. 5.4.1 Deploying the Services API The Relativity Services API is a web service based on Windows Communication Foundation (WCF), and it is hosted on IIS. The Services API has its own virtual directory and runs in its own application pool. In addition, it can be installed with the WebAPI as part of the Relativity web server component. The Services API is installed on the Relativity web server as an additional IIS application, called Relativity.Services. Most of the configuration of Relativity.Services is determined by the settings in the web.config file of the Relativity.Services virtual directory. Some configuration options are also controlled through the Configuration table of the EDDS database. In addition, a local version of the Services API runs on every agent server in your environment. This service uses the same code and database connection as your other Services API servers. However, the Services API on the agent server is self-hosted inside the agent Windows service, so you don't need to install any IIS infrastructure on the servers. Since only the agents on these servers use the Services API, the service only listens on the loopback interface using a named pipe and TCP port 6867. No firewall changes or other configuration should be required in most cases. Note: If your environment already uses the TCP port 6867 for other software, you can update value for the port number in the ServicesAPIMetadataPortOnAgentServers setting in the Configuration table on the EDDS database. For more information about settings used for hosting the Services API on the agent servers, see the Configuration Table guide on the Relativity 8.1 Documentation site. 5.4.2 Transport protocols The Services API supports the following data transport protocols: n n n n HTTPS - All data transmissions are encrypted using Internet standard SSL technology. They are transmitted over TCP port 443 by default. HTTPS is the default transport configuration. HTTP - While the transport isn't encrypted, the messages are encrypted. Data is transmitted over TCP port 80 by default. Net.TCP - Data has a binary format rather than the XML used for HTTP/HTTPS configurations, and performs better than HTTP/HTTPS configurations. Since this transport protocol uses binary formatting that is proprietary to Microsoft and not interoperable with other SOAP stacks, it suitable only for clients written in .NET. Data is transmitted over TCP port 808 by default. Net.Pipe - Uses a shared-memory pipe between the client and server. It is the fastest of the transport protocol options used by the Services API, but is restricted to clients written in .NET, and to environments with clients and servers running on the same machine. 5.4.3 AuthenticationType classes supported by the RSAPIClient When you instantiate the RSAPIClient, the constructor takes an object of AuthenticationType as an argument. The Services API includes the following subclasses of the AuthenticationType class that you can use to specify Relativity | Services API Guide - 60 the type of authentication used for the connection to the RSAPIClient: n n n IntegratedAuthCredentials - indicates the use of integrated Windows authentication. TokenCredentials - indicates the use of token authentication. This authentication type requires you to supply a token. UsernamePasswordCredentials - indicates the use of username and password authentication. This authentication type requires you to supply a username and password. For code samples illustrating how to use these authentication types 5.4.4 Summary of authentication methods and endpoint types The following table lists the combinations of transport and authentication types supported by the Services API. Endpoint Type HTTPS HTTP Net.TCP Net.Pipe Username and Password Supported Supported Supported Supported Authentication Type Windows Authentication Supported Supported Supported Supported 5.5 Creating the RSAPIClient proxy With the RelativityServices API, you can creating a proxy that uses transport protocols, such HTTPS, HTTP, Net.TCP, or Net.Pipe (.NET named pipes). The RSAPIClient class simplifies this process by providing an overloaded constructor that takes Uri and AthenticationType objects, as well as client-side override settings specified in an RSAPIClientSettings object. Note: For more information about the deprecated ArtifactManagerProxy class, see the Relativity 8 Developers site. 5.5.1 RSAPIClient class overview The RSAPIClient class exposes all of the available Services API functionality by connecting to the Authentication, DataManipulation, SetExecutor, and FileTransfer services. You can instantiate this class to create a proxy that includes methods used to interact with DTOs and other artifacts. 5.5.1.1 Properties and methods on the RSAPIClient class The RSAPIClient class has the following properties: n n n APIOptions - provides instance of APIOptions class associated with this proxy instance. See APIOptions class on the next page. AuthType - retrieves information about the authentication type used by the proxy to connect to the Services API. It is an AuthenticationType object. See AuthenticationType classes supported by the RSAPIClient on the previous page. EndpointUri - indicates the URI for the Services API URI of Relativity.Services running on the IIS. For Relativity | Services API Guide - 61 n example, it uses the format "http://localhost/Relativity.Services". Repositories - provides access the group of repositories that support typed data transfer objects. This class includes a comprehensive set of methods, which you can use to log in, create Artifact requests, query for Artifacts, and perform other tasks. 5.5.2 APIOptions class The APIOptions class controls who makes a call for any given method, where this user is making the call to, and how that call behaves. The members of this class include the following properties: n Token – represents an authenticated user. This property is automatically populated with a token value when the user is authenticated so you don’t need to explicitly call a login method. However, if you want to switch to another user, you can call the Login(), LoginWithCredentials(), or TokenLogin() to authenticated the new user. For sample code, see Token login on page 65 . n n WorkspaceID – represents the unique identifier for a workspace in Relativity. A value of -1 indicates the master EDDS database. StrictMode – determines the convention used when returning or supplying fields to the Create(), Read (), Update(), Delete(), and Query() methods. When this field is True, a subset of fields (using consistent names and datatypes) are available across the various methods. This property is False by default. For more information, see StrictMode property and Field directives on page 73. Note: Some Fields available in Relativity 7.4 may have different names or may be unavailable when this behavior is enabled by setting the property to True. 5.5.3 RSAPIClientSettings class You can programmatically set client-side configuration by passing an RSAPIClientSettings object to the overloaded constructor for the RSAPIClient class. This RSAPIClientSettings class can be used to define the CertificateFindValue and the CertificateValidation settings. See Setting client configuration overrides on page 19. 5.5.4 RSAPIClientServiceOperationFailed event RSAPIClientServiceOperationFailed event is raised when the RSAPIClient throws an exception due to a failure or operation error. See RSAPIClientServiceOperationFailed event in the Services API class libraries. 5.5.5 Best practices for proxy creation Use these guidelines when you create the proxy: n Create your proxy within a using statement in your code. n Use an appropriate AuthenticationType object to specify the login type. Note: If you use an AuthenticationType object, the proxy automatically logs in to the Services API so you don't need to call a login method. See AuthenticationType classes supported by the RSAPIClient on page 60 and Token login on page 65. Relativity | Services API Guide - 62 n n n Omit additional code for logging out or closing the session, since the RSAPIClient proxy automatically completes these tasks. Use the methods in the Relativity API Helpers to create the proxy in agents, custom pages, and event handlers. See Creating the proxy in an agent, custom page, or event handler below. To configure the Services API manually for agents, custom pages, and event handlers, use the guidelines provided in Manually configuring the Services API for the Relativity platform on page 18. These guidelines highlight the use of Relativity API Helpers for obtaining the Relativity.Services URL. 5.5.6 Creating the proxy in an agent, custom page, or event handler You can use the methods available in the Relativity API Helpers to create the proxy in agents, custom pages, or event handlers. The CreateProxy() method is available on the IServicesMgr interface in the Relativity API namespace. In your code, you call this method on the object returned by the GetServiceManager() method, which is available on the agent, custom page, and event handler helper classes also provided in the Relativity API Helpers. For more information, see Getting Started with the Relativity API Helpers in the Relativity 8.1 Developers site. On the Relativity 8.1 Developers site, see these pages for code samples that illustrate how to create the proxy in the agents, custom pages, or event handlers that you develop: n n n Creating custom agents Creating custom pages Getting started with event handlers includes links to code samples for specific event handler types, such as Pre Delete event handlers and others 5.5.7 Code samples for proxy creation in a console application These code samples illustrate how to create the proxy using each of the supported protocols. You specify the protocol in the string used to create the Uri object. For convenience, the code samples use IntegratedAuthCredentials, which is integrated Windows authentication. n Basic proxy creation try { using (IRSAPIClient proxy = new RSAPIClient(new Uri("http://localhost/Relativity.Services"), new IntegratedAuthCredentials())) { // Add your custom code. } } catch (Exception ex) { Console.WriteLine(ex.Message); } Relativity | Services API Guide - 63 n Basic proxy creation with HTTPS endpoint try { using (IRSAPIClient proxy = new RSAPIClient(new Uri("https://localhost/Relativity.Services"), new IntegratedAuthCredentials())) { // Add your custom code. } } catch (Exception ex) { Console.WriteLine(ex.Message); } n Basic proxy creation with HTTP endpoint try { using (IRSAPIClient proxy = new RSAPIClient(new Uri("http://localhost/Relativity.Services"), new IntegratedAuthCredentials())) { // Add your custom code. } } catch (Exception ex) { Console.WriteLine(ex.Message); } n Basic proxy creation with Net.TCP endpoint try { using (IRSAPIClient proxy = new RSAPIClient(new Uri("net.tcp://localhost/Relativity.Services"), new IntegratedAuthCredentials())) { // Add your custom code. } } catch (Exception ex) { Console.WriteLine(ex.Message); Relativity | Services API Guide - 64 } n Basic proxy creation with a Net.Pipe endpoint try { using (IRSAPIClient proxy = new RSAPIClient(new Uri("net.pipe://localhost/Relativity.Services"), new IntegratedAuthCredentials())) { // Add your custom code. } } catch (Exception ex) { Console.WriteLine(ex.Message); } 5.6 Token login Within the Relativity platform, you can use tokens for establishing connections in the following ways: n n Services API – To log in to the Services API, you create the proxy using the TokenCredential authentication type. You can also create a proxy and log in to the Services API by using the Relativity API Helpers. The helper classes facilitate creating the proxy and retrieving this session token from an agent, custom page, or event handler. They use this one-time session token as a parameter in the TokenLogin () method on the RSAPIClient class. As a best practice, use the helper classes to facilitating creating a proxy and authenticating to the Services API. For more information, see Getting Started with the Relativity API Helpers. Relativity – You can use a token to log in to the Relativity interface. The RSAPIClient class has the GenerateRelativityAuthenticationToken() method that you can use to obtain an authorization token for this purpose. 5.6.1 Creating the proxy using a token You can perform a token login by creating the proxy with the TokenCredential authentication type. When you specify the AuthenticationType, the proxy automatically logs in to the Services API so you don't need to call a login method. For information about creating the proxy in an agent, custom page, or event handler, see Getting Started with the Relativity API Helpers. The following code sample illustrates how to use this authentication type when creating the proxy. try { using (IRSAPIClient proxy = Relativity | Services API Guide - 65 new RSAPIClient(new Uri("http://localhost/Relativity.Services"), new TokenCredential(token))) { // Add your custom code. } } catch (Exception ex) { Console.WriteLine(ex.Message); } 5.6.2 Generating an authorization token for Relativity You can use the GenerateRelativityAuthenticationToken() method on the RSAPIClient class to obtain a token for logging in to the Relativity interface. This authorization token is generated from a session token obtained from proxy. For more information, see APIOptions class on page 62. Note: You can also appended this authorization token to an HTTP query string as authToken=<value>. For more information, see REST API authentication. public void GenerateRelativityAuthenticationToken() { try { // Step 1: Create a proxy, using either UsernamePasswordCredentials, IntegratedAuthCredentials, // or TokenCredentials. using (IRSAPIClient proxy = new RSAPIClient(new Uri("net.pipe://localhost/Relativity.Services"), new IntegratedAuthCredentials())) { // STEP 2: Generate Relativity authorization token from session token and return it in ReadResult object. ReadResult readResult = proxy.GenerateRelativityAuthenticationToken (proxy.APIOptions); // STEP 3: Pull token out of ReadResult object for use as needed. if (readResult.Success) { string authToken = readResult.Artifact.getFieldByName ("AuthenticationToken").ToString(); Console.WriteLine(String.Format("Successfully generated Relativity authorization token {0}", authToken)); } Relativity | Services API Guide - 66 else { Console.WriteLine(String.Format("An error occurred generating Relativity authorization token: {0}", readResult.Message)); } } } catch (Exception ex) { Console.WriteLine(String.Format("An error occurred generating Relativity authorization token: {0}", ex.Message)); } } 5.7 Data Transfer Objects (DTOs) The Services API now includes Data Transfer Objects (DTOs) that simplify coding and minimize errors by providing typed wrappers for system Artifacts and Fields. Note: While the Services API continues to provide support for the untyped layer, the use of DTOs is recommended for any new development. 5.7.1 DTO Features DTOs provide the following features as well as offering typed wrappers for system Artifacts and Fields: n n Client methods organized into typed Repositories, such as DocumentRepository, BatchRepository, and others Consistent field values based on the type of the Field. In conjunction with DTOs, you can set the StrictMode property on the APIOptions class, which causes the Services API to return consistent Fields for the Read() and Query() methods across all ArtifactTypes. Note: Some Fields available in Relativity 7.4 may have different names or may be unavailable when this behavior is enabled by setting the property to True. By default, StrictMode is set to False so the Services API continues to return the same Fields as in Relativity 7.4. See StrictMode property and Field directives on page 73. n n n n n n ParamArray versions of many List parameters Automatic use of the APIOptions instance held in the RSAPIClient class, so no manual coding is required. See Creating the RSAPIClient proxy on page 61. Constants for system Artifact and Field names Deep field retrieval on explicit request Casting methods for dynamic Fields Eliminates use of byte array for string values This functionality is available in the kCura.Relativity.Client.DTOs and other namespaces. Relativity | Services API Guide - 67 5.7.2 Supported DTOs You can use the following table to locate code samples for DTOs. The table lists the Relativity version when the support for a specific operation was first introduced. Click the version number under an operation to display a code sample for it. DTO Create Read Batch on page 82 7.5 BatchSet on page 7.5 7.5 85 Choice on page 95 7.5 Client on page 97 7.5 7.5 Document on 7.5 7.5 page 103 Error on page 112 7.5 Field on page 113 7.5 7.5 Folder on page 7.5 7.5 153 Group on page 7.5 7.5 158 Layout on page 7.5 163 MarkupSet on 7.5 7.5 page 166 ObjectType on 7.5 7.5 page 172 RDO on page 177 7.5 7.5 Relativ7.5 ityApplication on page 186 RelativityScript on 7.5 page 189 Tab on page 196 7.5 User on page 198 7.5 7.5 View on page 207 7.5 Workspace on 7.5 page 210 Supported Operations (listed by earliest supported version) Update Delete Query Other 7.5 7.5 7.5 7.5 7.5 Cancel - 7.5 Purge - 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 Download native file - 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 Execute - 7.5 Retrieve input -7.5 7.5 7.5 7.5 7.5 The operations listed in the previous table are supported at the following levels for DTOs: n n n Master database - Workspace, Client, User, and Group Master database and Workspace - RelativityScript and Choice Workspace - all other ArtifactTypes Relativity | Services API Guide - 68 Note: For Error, the Create() method is supported at the master database and Workspace levels. However, all Errors are written to the master database, and the Workspace where the Error occurred is inferred from the WorkspaceID value set in the APIOptions instance. 5.8 Untyped base Artifacts The RSAPIClient supports a limited set of operations on untyped base Artifacts, and Fields. The use of untyped Artifacts and Fields was required by Services API in Relativity 7.4 or below. Note: While the Services API continues to support the untyped layer, use DTOs to simplify development by eliminate the need for casting. Implement any new development with DTOs. See Data Transfer Objects (DTOs) on page 67. 5.8.1 RSAPIClient support for ArtifactTypes The following table lists the ArtifactTypes and operations supported in the untyped layer. ArtifactTypes Create Batch on page 79 BatchSet on page 79 Choice on page 79 Client on page 79 Document on page 80 Error on page 80 Field on page 80 Folder on page 80 Group on page 80 Layout on page 80 MarkupSet on page 80 ObjectType on page 80 RelativityApplication on page 80 Relativity Dynamic Object (RDO) on page 80 RelativityScript on page 80 Tab on page 80 User on page 80 View on page 80 Workspace on page 81 Yes Yes Yes Yes Limited support for Create() on Fields on the next page Yes Yes Yes Yes Read Yes Yes Yes Yes Yes Yes Yes Query Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Relativity | Services API Guide - 69 Supported Operations Update Delete Yes Yes Yes Yes Yes Yes 5.8.2 Limited support for Create() on Fields The Services API supports the Query() method and a limited version of the Create() method on the Field system type. The Create() method doesn't currently support the creation of the Decimal and Currency fields through the API. Allow Group By Allow HTML Allow Pivot Allow Sort/Tally Associative Object Type Available In Field Tree Available In Viewer Field Type Friendly Name Properties Supported for Create() on Field Identifier Open to Associations Import Behavior Order Include in Text Index Pane Icon Keywords Relational Length Required Linked Unicode Name Width Notes Wrapping Object Type Artifact Type ID Yes/No Field Display Values When a Field is created through the Services API, the unsupported properties are set to their default values. Properties Set to Default Values Field Tree View Popup Picker View Filter Type Propagate to Formatting Relativity Applications Keyboard Shortcut 5.8.3 Field (untyped) Relativity uses fields to store document and other metadata, as well as coding selections made by a reviewer. It provides multiple Field types to support this functionality, which are also available through the Services API. 5.8.3.1 System type fields System type fields are properties on the base Artifact class. In the untyped layer, the base Artifact has the following properties: ArtifactID ArtifactGuids ArtifactTypeID Base Artifact Properties in the Untyped Layer ArtifactTypeName ArtifactTypeGuids ParentArtifactID Since system type Fields don't have IDs, you must request them by name. Certain Fields have both a display name used in the Relativity web UI, and an SQL column name used to reference the Field in the database. For example, Field has a property with the display name Created By that contains a username as a String, and an SQL column name of CreatedByName. Another property has no display name, but an SQL column name of CreatedBy, which returns the user’s ID. You can use either name when requesting Fields during a read or query operation. The Services API returns the Field using the name referenced in your request. If you don't specify any Fields in a request, the API will Relativity | Services API Guide - 70 return all Fields using the display names when they exist and the SQL names when no display names are available. You can also set the StrictMode to maintain consistency on the Fields returned by the Services API, and field directives to indicate when you want all or no Fields returned. 5.8.3.2 Using Field types in the untyped layer Field types are handled differently depending on whether you work with them through the DTO or untyped layer. While the Services API continues to support the untyped layer, the use of DTOs is recommended for any new development. FixedLengthText and LongText Fields In Relativity, a long text field is larger than 4,999 characters, while a fixed length fields is less than or equal to this number of characters. In the untyped layer, all strings are encoded as byte arrays. The .NET client is provided with the GetString() method, which converts a byte array into a String. These FixedLengthText and LongText fields are handled in this way because the Services API is exposed as a set of SOAP Web Services. The content of all fields is serialized as XML while being transported from the server to the client. XML enforces restrictions on the characters that may be used in strings. Consequently, the Services API doesn't use the XML string data type since fields within Relativity may contain text extracted from documents, containing characters prohibited by XML. SingleChoice Fields In Relativity, a single choice field has a predetermined set of values called choices. A user can only select one of these choices for coding or other purposes. Use these guidelines for SingleChoice fields: n Create or update SingleChoice fields on documents or RDOs. You can set the values on these Fields to an ArtifactID or GUID, but not to a text representation of a Choice. For example, you can set the value of the Field Single Choice Field on a Document or Dynamic Object to a Choice using its ArtifactID (such as 100456) or its GUID. The following sample code illustrates how to set a SingleChoice in the untyped layer: artifactRequest.Fields.Add(new Field("Single Choice Field", 100456)); n Retrieve a list of valid Choices for a SingleChoice field as follows: n Perform a read operation using the ArtifactID for Single Choice Field and the ArtifactTypeName of Field (or ArtifactTypeID = 14). o Request that the Choices field is returned. A List of kCura.Relativity.Client.Choice objects is returned. Read operations on a SingleChoice field return the following data: o o o o o Type of the Value property is kCura.Relativity.Client.Choice Text value of the Choice set on the Field is the Name property of the Choice object ArtifactID property of the Choice object is the ArtifactID of the Choice definition within the Workspace ArtifactGuids property will be a List of GUIDs assigned to Choice definition Relativity | Services API Guide - 71 MultiChoice Fields In Relativity, a multiple choice field has a predetermined set of values called choices. A user can select several choices for coding or other purposes. Use these guidelines for MultiChoice fields: n n Set the values of a MultiChoice Field by defining a MultiChoiceUpdateValue that includes in its Value property a List of Integers, which are the ArtifactIDs of the individual Choices in the MultiChoice Field. Alternatively, provide the ValueAsGuid property, which is a List containing a List of GUIDs that represent the ArtifactIDs of the individual Choices. The value is updated based upon the MultiChoiceUpdateValue.Method property. It is replaced with new values or merged with existing ones. See MultiChoiceUpdateValue in the Services API Class Library. Read operations for a MultiChoice Field on a Document or Dynamic Object return the selected values as a .NET generic list (that is List<Choice>). SingleObject Fields Relativity uses a SingleObject field to define a one-to-many relationship between two objects. Use these guidelines for SingleObject fields: n n Set a a SingleObject field in the untyped layer by passing the Integer value of the ArtifactID as the field value. Read or query operation return a SingleObject field as an Int32. The value of this field is the ArtifactID of the Object. MultipleObject Fields Relativity uses a multiple object field to define a many-to-many relationship between two objects. Use these guidelines for MultiObject fields: n n Set a MultipleObject field by passing a List of Int32 values for the Artifact IDs. Read or query operations return a MultipleObject field as an Int32. The values for this field are the ArtifactIDs of the objects. User Fields In Relativity, a user field contains Relativity users with rights to the current workspace. Use these guidelines for User fields: n n Create and update operations require you to set the User field to an ArtifactID. On read and query operations, the Value property of the field has the type kCura.Relativity.Client.User, which provides both the Name and the ArtifactID of the user. 5.8.3.3 Querying for Fields associated with an ArtifactType You can use the Query() method to retrieve ArtifactIDs and the Names of all Fields associated with a specific ArtifactType. The search returns only Fields that the logged in user has permissions to view. The Field Type field supports only TextCondition. //STEP 1: Create a Query to describe the search you want to run. Query q = new Query(); Relativity | Services API Guide - 72 //STEP 2: Use an ArtifactType enum to set the ArtifactTypeID based on the type of item that you want to query on. Set the ArtifactTypeName to the name of the item type. q.ArtifactTypeID = (int) ArtifactType.Field; q.ArtifactTypeName = "Field"; //STEP 3: Create a Fields list to indicate which fields you want returned. q.Fields.Add(new Field("Name")); q.Fields.Add(new Field("Artifact ID")); q.Condition = null; q.RelationalField = null; q.Condition = new TextCondition("Object Type", TextConditionEnum.EqualTo, "Document"); 5.9 StrictMode property and Field directives In the Services API, you can use the StrictMode property and Field directives to control how the Fields collection on an Artifact is handled when it is returned. 5.9.1 StrictMode property for DTOs The StrictMode property is used to provide consistency in the Fields returned across all ArtifactTypes. When you enable StrictMode, Fields have the following behavior through the Services API: n n n n n n n Consistent population and naming of system type fields, including System Created By, System Created On, System Last Modified By, System Last Modified On, and Relativity Text Identifier. Text fields returned as Strings. ArtifactID is available on the base Artifact. Read() and Query() methods return the same Fields collection. Create() and Update() methods use the same Field names as those returned by Read and Query. User fields are always returned as a User object with an ArtifactID and Full Name. Choice fields are always returned as a Choice object with an ArtifactID and Name. StrictMode is a Boolean property on the APIOptions object: proxy.APIOptions.StrictMode = True 5.9.2 Field directives for DTOs The Services API provides the AllFields and NoFields directives to ensure that the Fields collection on an Artifact is handled consistently when returned across supported operations: n n AllFields – Use this directive to return all of the Fields on an Artifact. NoFields – Use this directive when you don't want to return any of the Fields on an Artifact. Only the base Artifact is populated. You can use the AllFields and NoFields directives on the read and query operations for DTOs. Read operation using AllFields: Relativity | Services API Guide - 73 DTOs.RDO dto = new DTOs.RDO(1036225); dto.Fields = FieldValue.AllFields; Query operation using NoFields: DTOs.Query<DTOs.RDO> query = new DTOs.Query<DTOs.RDO>(); query.Fields = FieldValue.NoFields; The SelectedFields directive is available for use with a SavedSearchCondition or ViewCondition on a query. For more information, see SavedSearchCondition on page 229 or ViewCondition on page 233. 5.9.3 StrictMode and the TextIdentifer property (untyped layer) The Services API provides a TextIdentifier property that displays the Identifier field of an object. For example, TextIdentifier equals the Name field on instances of Dynamic Objects. You can use it in place of the Name property on Dynamic Objects. If you set both the TextIdentifier and Name properties during object creation, their values must be equal or you will receive an error message. In the untyped layer, when StrictMode is set to True, the ArtifactRequest and query operations return a field called Relativity Text Identifier. These operations don't consistently return the TextIdentifier property when StrictMode is False. Use the constant kCura.Relativity.Client.Constants.RELATIVITY_TEXT_IDENTIFIER to reference the Relativity Text Identifier field. 5.9.4 Field directives (untyped layer) The Services API provides the AllFields and NoFields directives to ensure that the Fields collection on an Artifact is handled consistently when returned across supported operations: n n AllFields – Use this directive to return all of the Fields on an Artifact. NoFields – Use this directive when you don't want to return any of the Fields on an Artifact. Only the base Artifact is populated. Note: The SelectedFields directive is available for use with a SavedSearchCondition or ViewCondition on a query. You can use the AllFields and NoFields directives on the read and query operations for the untyped layer. Read operation with AllFields: ArtifactRequest artifactRequest = new ArtifactRequest(); artifactRequest.Fields = Field.AllFields; Query operation with NoFields: Relativity | Services API Guide - 74 Query query = new Query(); query.Fields = Field.NoFields; 5.10 Best practices for the Services API Use these guidelines to optimize your application development with the Services API. 5.10.1 Use DTOs whenever possible Use of strongly typed DTOs as the preferred programming model for the Services API. DTOs provide type-safe access to common Relativity object types. In addition, they offer the benefit of consistent Field names and result datatypes. See Data Transfer Objects (DTOs) on page 67. Note: While the Services API continues to support the use of the ArtifactManagerProxy as an access method, the DTOs provide similar functionality as well as these additional benefits. 5.10.2 Bring back only Fields that you need for optimum performance When you make request for all Fields on an Artifact, it increases the overhead on your application. To ensure optimum performance, don’t use the AllFields directive unless your client side-code requires every Field on an Artifact. Avoid returning Fields that your code never uses. See Field directives for DTOs on page 73. 5.10.3 Don’t use the Services API to bulk-load data Avoid or minimize the use of the Services API when loading large amounts of data into Relativity. In general, the Services API is designed for end-user application access patterns. Although it does have the ability to bulkload data, the Services API isn’t optimized for that use case. Consider using the Import API when you have large amounts of data to load. Loading data through the Services API and Import API is complementary and can be used within the same application. Try benchmarking both approaches in your environment so that you can determine which API offers the optimum performance. See the Import API on the Relativity 8.1 Developers site. 5.10.4 Work in batches Use batches to send medium-to-large data sets to the Services API. Break your create operations into batch sizes appropriate for your data and environment. Batches of 1,000 objects are far better than batches of 1 million. For example, single request to create 10,000Dynamic Objects is hard on memory and bloats your message payload. You may even reach the configured limits for maximum message size, which you want to avoid doing even though this setting can be overridden through server configuration values. 5.10.5 Use GUIDs to reference Fields and object types Make your custom applications resilient by using unique identifiers (GUIDs) when referring to Fields, object types, and Choices. This approach offers significant advantages over programming against the name of an Artifact when using the Application Deployment System (ADS) to develop custom applications. It avoids name conflicts that can occur when Artifacts from a custom application are imported into a Relativity workspace, Relativity | Services API Guide - 75 containing objects with similar names. The ADS provides the ability to rename Fields during import, since it assigns a GUID to each Artifact. While a powerful technique for working with custom applications, it also requires correct handling. The use of GUIDs simplifies development since changes in the names of Fields and object types can occur at any time. You can find the GUIDs in the custom application XML that you export from Relativity. On your development machine, you can also search the ArtifactGuid table for a workspace to find a GUID for a specific Artifact. We recommend that you add GUIDs to a constants class in your code base and use those constants in your code. See GUIDs in application development on page 78. 5.10.6 Install and uninstall applications through the ADS When building your application, take advantage of the ADS provided by Relativity. The ADS is designed for packaging your schema into an application and deploying it to the workspace you are developing against. While you can create object types and Fields using the Services API, this system provides powerful tools for setting up your schema in a workspace. See Application Deployment System guide. 5.10.7 Use of the APIOptions token property With the inclusion of the APIOptions as a property on the RSAPIClient class, you don't need to explicitly track the token returned from the Login(), LoginWithCredentials(), or TokenLogin() method. A successful call to either of these methods automatically populates the Token property of the APIOptions property. Note: The Token property is automatically populated with a token value when the user is authenticated so you don’t need to explicitly call a login method. However, if you want to switch to another user, you can call one of the login methods to authenticated the new user. See the following code sample: try { proxy.Login(); } catch (Exception ex) { throw new Exception(String.Format("An error occurred logging in: {0}", ex.Message), ex); } Console.WriteLine("APIOptions.Token is {0}", proxy.APIOptions.Token); The result of the call to Login() method isn't explicitly stored, and it is automatically available via the Token property of APIOptions. 5.10.8 Avoid specific version assembly references When you are developing with a reference to the kCura.Relativity.Client assembly, avoid using the Specific Version reference on the Reference Properties window in Visual Studio. Relativity | Services API Guide - 76 When a custom assembly is loaded for an event handler, agent, or custom page, common .dll files (such as kCura.Relativity.Client.dll) are automatically copied into the domain from the lib folder. If you set the Specific Version option in Visual Studio, it may not match the version of the .dll files installed on the environment. This mismatch may prevent your application from executing properly, if at all. Note: Don't package the kCura.Relativity.Client.dll with your application. If you package this .dll file with your application, it may be overwritten or ignored when you deploy the application in Relativity. 5.10.9 Use constant Field names for Read() and Query()methods When calling the Read() and Query() methods on DTOs, you can reference the constant strings assigned to Field names. See Constant Field names on page 146 You can also use the AllFields directive while in the development phase to discover available Fields: FieldValue.AllFields. 5.10.10 Use Services API enumerations or constants The Services API provides enumerations and constants that you can use instead of strings or integers in your code. For example, this code illustrates how to use the constant DescriptorArtifactTypeID on the ObjectTypeFieldNames class instead of the string "Descriptor Artifact Type ID": objectTypeQuery.Condition = new WholeNumberCondition(ObjectTypeFieldNames.DescriptorArtifactTypeID, NumericConditionEnum.EqualTo, 1000035); For more information, see ArtifactType under the kCura.Relativity.Client namespace, or ObjectTypeFieldNames under the kCura.Relativity.Client.DTOs namespace in the Services API class library. Relativity | Services API Guide - 77 5.11 GUIDs in application development You can use a Globally Unique Identifiers (GUIDs) to provide a unique reference numbers for Artifacts. For example, you can use GUIDs to identify the Relativity Dynamic Objects (RDOs) that you create for your custom applications. When you add RDOs to an application, Relativity automatically generates and assigns GUIDs and ArtifactIDs to them. 5.11.1 Using GUIDs as best practice As a best practice, you should use GUIDs in your application development. They offer the following advantages: n n n Consistency - They provide consistent references across Relativity workspaces and environments. They avoid the confusion that may occur when Field names or ArtifactIDs may change across workspaces. Enhanced portability - Since you can install custom applications in multiple workspaces, GUIDs ensure that the RDOs retain the same unique identifier across workspaces, while ArtifactIDs vary by workspace. Easier application management - Field names can be modified after an application is installed in a workspace. By referencing GUIDs, you have the ability to manipulate these fields even though their names may vary across workspaces. 5.11.2 Using GUIDs in read, update, and delete operations After you install a custom application, you can use GUIDs to perform read, update, and delete operations on Choice, Field, ObjectType and RDOs as follows: n n ArtifactGuid can be substituted for ArtifactID. ArtifactTypeGuid can be substituted for ArtifactTypeID. You can also use GUIDs for operations on instances of RDOs: n n Create - ArtifactTypeGuid can be substituted for ArtifactTypeID or ArtifactTypeName. Read, update, and delete - ArtifactGuid can be substituted for ArtifactID. For code samples, see RDO on page 177. 5.11.3 Viewing GUIDs for your application components When you enable Developer mode for Relativity, you can view the GUIDs for the components in your applications. You can click the Show Component GUIDs link available on the Relativity Applications tab to view a list of GUIDs. For more information, see Development environment guidelines on the Relativity 8.1 Developers site. 5.12 Asynchronous framework The asynchronous framework is used to monitor long-running operations by returning status updates as events. Not only does the asynchronous framework provide you with ability to monitor these operations, but you can also use it to cancel them. The asynchronous framework supports the following processes: n n Creating BatchSets using the CreateBatchesforBatchSet() method Purging BatchSets using the PurgeBatchesOfBatchSet() method Relativity | Services API Guide - 78 After the asynchronous version of these operations starts a process and succeeds, it returns a unique identifier for that operation. The property is empty when the operation fails. Each of these operations (CreateBatchesforBatchSet() and PurgeBatchesOfBatchSet() methods) has two versions: one that registers by default and one that takes a flag. Depending on the version of the operation, the proxy is registered to monitor the state of the process, and to raise events as its status changes. You can call the MonitorProcessState() method to explicitly register the proxy to monitor and raise status events for a running process. For code samples, see BatchSet on page 85. The asynchronous framework will raise events for status updates automatically. However, you can also retrieve the process status explicitly by calling the GetProcessState() method on the proxy. This method returns a ProcessInformation object that contains data about the internal state of the process. You can cancel an asynchronous process or allow it to run until it completes or fails. To perform this action, call the FlagForCancellation() method, which flags the process for cancellation when the current batch completes. 5.13 Terminology The following table lists terms that are frequently used to refer to features and functionality available in the Services API and the Relativity web UI. Term APIOptions ArtifactID ArtifactManagerProxy ArtifactRequest Artifact ArtifactType ArtifactTypeID Batch BatchSet Choice Client CRUD Description An object containing a login token representing a session as well as the current WorkspaceID. Used as a parameter for most methods on the proxy. A 32-bit integer that uniquely identifies items in a workspace. (Deprecated - See RSAPIClient class overview on page 61.) The client-side proxy that lives in the kCura.Realtivity.client.dll. Used to communicate with the Services API by calling methods on it. A request for an operation to be performed on an Artifact in a workspace. It can hold information about documents or Dynamic Objects. Used in CRUD operations. Objects representing a content item in Relativity, such as a document. It has a unique identifier called an ArtifactID. An enumeration that assigns a value to various Artifacts in Relativity. An integer used to identify the type of an Artifact in a workspace. For example, Documents have an ArtifactTypeID of 10. A collection of documents assigned to a single user for review. A collection of multiple batches originating from the same data source. A predetermined value assigned to a single or multiple-choice field. An organization associated with users, matters, and workspaces. Represents the following database operations: create, read, update, and delete. Relativity | Services API Guide - 79 Term DescriptorArtifactTypeID Document Error Field Folder Group Layout MarkupSet ObjectRules ObjectType ParentArtifactID Query QueryResult RelativityApplication Relativity Dynamic Object (RDO) RelativityScript RSAPIClient SavedSearch System Types Tab User View Description The name of the property on the ObjectType DTO, which represents the effective ArtifactTypeID. If an ArtifactTypeID is required in the code, that ID can simultaneously be thought of as the DescriptorArtifactTypeID of some ObjectType. An object in Relativity with properties such as native files, extracted text, images, and other metadata. Provides information about an error that occurred in Relativity. An object used to store metadata about another object. Similar to columns in a database. A parent container for documents displayed in the Folder browser on Documents tab. A collection of one or more users, often assigned a specific set of permissions. A web-based coding form that allows a user to edit the data stored in the fields of an object. A set of annotations and redactions used during a review. Provides a list of rules that control the specific behavior of a Dynamic Object. A Dynamic Object type added to a workspace. Synonymous with ArtifactType (as in ArtifactTypeID). A unique identifier for a parent of an Artifact. A query or search of a Workspace. It is passed to the Query() method. Data returned by the Query() or SubQuery() methods. A collection of object types, fields, choices, views, layouts, tabs, event handlers, object rules, Relativity scripts, and custom pages designed to provide custom functionality when deployed in a workspace. An object created by a Relativity administrator. Each type of Dynamic Object has a unique ArtifactTypeID in the workspace where it was created. A script defined through the Scripts tab in Relativity that allows the execution of custom SQL scripts. Refers to the RelativityServices API Client (RSAPIClient) for .NET. The clientside proxy that lives in the kCura.Realtivity.client.dll. Used to communicate with the Services API by calling methods on it. See RSAPIClient class overview on page 61. A search that is defined and stored in Relativity for repeated use. Refers to the properties on the base Artifact, which contains Artifact ID, System Created By, System Created On, System Last Modified By, and System Last Modified On. A tab in the Relativity web UI. Individuals with access to Relativity. A user must be a member of group to access a workspace. Defines a set of criteria to control the contents, fields, and sort order of items displayed on list pages in Relativity. Relativity | Services API Guide - 80 Term Workspace Description A secure data repository for documents. It also contains views, layouts, fields, choices, and other objects. Dynamic Objects and applications may be added to workspaces. 6 DTO reference and code samples DTOs are typed wrappers for Artifacts supported by the Services API. For more information, see Data Transfer Objects (DTOs) on page 67. You can use the following table to locate code samples for DTOs. The table lists the Relativity version when the support for a specific operation was first introduced. Click the version number under an operation to display a code sample for it. DTO Create Read Batch on the next 7.5 page BatchSet on page 7.5 7.5 85 Choice on page 95 7.5 Client on page 97 7.5 7.5 Document on 7.5 7.5 page 103 Error on page 112 7.5 Field on page 113 7.5 7.5 Folder on page 7.5 7.5 153 Group on page 7.5 7.5 158 Layout on page 7.5 163 MarkupSet on 7.5 7.5 page 166 ObjectType on 7.5 7.5 page 172 RDO on page 177 7.5 7.5 Relativ7.5 ityApplication on page 186 RelativityScript on 7.5 page 189 Tab on page 196 7.5 Relativity | Services API Guide - 81 Supported Operations (listed by earliest supported version) Update Delete Query Other 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 Cancel - 7.5 Purge - 7.5 Download native file - 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 7.5 Execute - 7.5 Retrieve input -7.5 DTO Create Read User on page 198 7.5 7.5 View on page 207 7.5 Workspace on 7.5 page 210 Supported Operations (listed by earliest supported version) Update Delete Query Other 7.5 7.5 7.5 7.5 7.5 6.1 Batch In Relativity, an administrator creates a batch by splitting a static set of documents into multiple sets based on the criteria for a review. For more information, see Batches in the Relativity 8.1 Documentation site. The Services API supports read, update, delete, and query operations on the Batch DTO. 6.1.1 Updating and reading a Batch When you update a Batch object, you can modify only the AssignedTo and BatchStatus properties. This code samples illustrates how to modify these properties using the Update() method on the Batch repository. public static bool Batch_Update_Using_Repository(IRSAPIClient proxy) { // STEP 1: Create an object specifying the Artifact ID // and the fields that you want returned. DTOs.Batch batch1 = new DTOs.Batch(1036607); batch1.Fields = FieldValue.AllFields; // STEP 2: Read current values. ResultSet<DTOs.Batch> results = new ResultSet<DTOs.Batch>(); try { results = proxy.Repositories.Batch.Read(batch1); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } if (!results.Success) { Console.WriteLine("Error: " + results.Message); return false; } // STEP 3: Get the Artifact from the read results. DTOs.Batch batch2 = results.Results.FirstOrDefault().Artifact; Relativity | Services API Guide - 82 Console.WriteLine("Batch Artifact ID: " + batch2.ArtifactID.ToString()); Console.WriteLine("Batch Text Identifier: " + batch2.TextIdentifier); Console.WriteLine("Batch Status ID: " + batch2.BatchStatus.Name + ", ID: " + batch2.BatchStatus.ArtifactID.ToString()); Console.WriteLine("Batch Assigned To: " + batch2.AssignedTo.FullName + ", ID: " + batch2.AssignedTo.ArtifactID.ToString()); // STEP 4: Modify properties of the Batch. // For a Batch, you can only modify the AssignedTo and BatchStatus properties. batch2.BatchStatus = new DTOs.Choice(1035249); batch2.AssignedTo = new DTOs.User(1016508); // STEP 5: Call the Update() method on the Batch repository. WriteResultSet<DTOs.Batch> writeResultSet = null; try { writeResultSet = proxy.Repositories.Batch.Update(new List<DTOs.Batch> { batch2 }); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } if (writeResultSet.Success) { Console.WriteLine("Batch updated successfully"); batch2 = results.Results.FirstOrDefault().Artifact; Console.WriteLine("Batch Artifact ID: " + batch2.ArtifactID.ToString()); Console.WriteLine("Batch Text Identifier: " + batch2.TextIdentifier); Console.WriteLine("Batch Status ID: " + batch2.BatchStatus.ArtifactID.ToString()); Console.WriteLine("Batch Assigned To ID: " + batch2.AssignedTo.ArtifactID.ToString()); } else { string message = writeResultSet.Message; if (message == null && writeResultSet.Results.Count > 0 && !writeResultSet.Results[0].Success) message = writeResultSet.Results[0].Message; Console.WriteLine("Error: " + message); return false; } Relativity | Services API Guide - 83 // STEP 6: Read back the updated Artifact. try { results = proxy.Repositories.Batch.Read(batch2); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } if (!results.Success) { Console.WriteLine("Error: " + results.Message); return false; } // STEP 7: Get the updated property values from the read results. DTOs.Batch batch3 = results.Results.FirstOrDefault().Artifact; Console.WriteLine("Batch Artifact ID: " + batch3.ArtifactID.ToString()); Console.WriteLine("Batch Text Identifier: " + batch3.TextIdentifier); Console.WriteLine("Batch Status: " + batch3.BatchStatus.Name + ", ID: " + batch3.BatchStatus.ArtifactID.ToString()); Console.WriteLine("Batch Assigned To: " + batch3.AssignedTo.FullName + ", ID: " + batch3.AssignedTo.ArtifactID.ToString()); return true; } 6.1.2 Querying for a Batch To query for a Batch, you can use the fields listed in the following table. For more information, see Querying on page 227. ArtifactID Assigned To Batch Batch Set Fields for Batch queries Batch Size Batch Status Batch Unit Reviewed This code sample illustrates how to set query conditions, call the Query() method on the Batch repository, and iterate through the result set. public static bool Batch_Query_By_BatchSet_Using_Repository(IRSAPIClient proxy) { Relativity | Services API Guide - 84 // STEP 1: Create criteria that identifies a BatchSet object. WholeNumberCondition criteria = new WholeNumberCondition (BatchFieldNames.BatchSet, NumericConditionEnum.EqualTo, 1036606); // STEP 2: Create a query that uses your criteria. DTOs.Query<DTOs.Batch> query = new DTOs.Query<DTOs.Batch>(); query.Condition = criteria; query.Fields = FieldValue.AllFields; // STEP 3: Call the Query() method on the Batch repository. QueryResultSet<DTOs.Batch> result = null; try { result = proxy.Repositories.Batch.Query(query); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } Console.WriteLine(string.Format("Number of batches returned: {0}", result.Results.Count)); // STEP 3: Iterate through returned the results. foreach (DTOs.Result<DTOs.Batch> batchResult in result.Results) { DTOs.Batch batch = batchResult.Artifact; Console.WriteLine("Batch Name: " + batch.Name); Console.WriteLine("Batch Artifact ID: " + batch.ArtifactID); } return true; } 6.2 BatchSet In Relativity, a batch set represents a group of batches, which are sets of documents. For more information, see Batches in the Relativity 8.1 Documentation site. The Services API supports all CRUD and query operations on the BatchSet DTO. 6.2.1 Creating, updating, and querying BatchSet objects After you create a BatchSet DTO, you can update its properties by calling the Update() method on the BatchSet repository. This code sample illustrates how to create a BatchSet, read its fields from the database, update the fields, and then query for the BatchSet. Relativity | Services API Guide - 85 public static bool Create_then_Update_A_BatchSet_Using_Repositories (IRSAPIClient proxy) { // STEP 1: Create a BatchSet DTO and set its properties. kCura.Relativity.Client.DTOs.BatchSet newBatchSet = new kCura.Relativity.Client.DTOs.BatchSet(); newBatchSet.Name = "My Batch Set"; newBatchSet.BatchPrefix = "Batch"; newBatchSet.MaximumBatchSize = 5; newBatchSet.AutoBatch = true; newBatchSet.MinimumBatchSize = 1; newBatchSet.AutoCreateRateMinutes = 30; newBatchSet.BatchDataSource = new kCura.Relativity.Client.DTOs.Artifact (1036604); // STEP 2: Call the Services API to create the BatchSet. WriteResultSet<kCura.Relativity.Client.DTOs.BatchSet> createResults = null; try { createResults = proxy.Repositories.BatchSet.Create(newBatchSet); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred creating the batch set: {0}", ex.Message)); return false; } if (!createResults.Success) { Console.WriteLine(string.Format("An error occurred creating the batch set: {0}", createResults.Message)); return false; } Int32 newID = createResults.Results[0].Artifact.ArtifactID; Console.WriteLine("ID of the new Batch Set: " + newID.ToString()); // STEP 3: Read the batch to get all the fields from the database. ResultSet<kCura.Relativity.Client.DTOs.BatchSet> readResults = null; kCura.Relativity.Client.DTOs.BatchSet batchSetRead = new BatchSet(newID); batchSetRead.Fields = FieldValue.AllFields; try { readResults = proxy.Repositories.BatchSet.Read(batchSetRead); Relativity | Services API Guide - 86 } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred reading the batch set: {0}", ex.Message)); return false; } if (!readResults.Success) { Console.WriteLine(string.Format("An error occurred reading the batch set: {0}", readResults.Message)); return false; } kCura.Relativity.Client.DTOs.BatchSet readBatchSet = readResults.Results [0].Artifact; Console.WriteLine("Initial value of Name: " + readBatchSet.Name); Console.WriteLine("Initial value of AutoCreateRate: " + readBatchSet.AutoCreateRateMinutes.ToString()); // STEP 4: Modify properties of the BatchSet. The Name and TextIdentifier both have the // same value which is the Batch Set Name. Since they have the same value, // you can update the BatchSet using the TextIdentifier. kCura.Relativity.Client.DTOs.BatchSet batchSet = readResults.Results [0].Artifact; batchSet.TextIdentifier = "Better Batchset"; batchSet.AutoCreateRateMinutes = 45; // MaximumBatchSize must be set for the update. batchSet.MaximumBatchSize = readBatchSet.MaximumBatchSize; WriteResultSet<kCura.Relativity.Client.DTOs.BatchSet> updateResults = null; // STEP 5: Call the Services API to update the BatchSet just created. try { updateResults = proxy.Repositories.BatchSet.Update(batchSet); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred updating the batch set: {0}", ex.Message)); return false; } Relativity | Services API Guide - 87 if (!updateResults.Success) { Console.WriteLine(string.Format("An error occurred updating the batch set: {0}", updateResults.Message)); return false; } // STEP 6: Verify that update worked by querying the BatchSet. Query<kCura.Relativity.Client.DTOs.BatchSet> batchSetQuery = new Query<kCura.Relativity.Client.DTOs.BatchSet>(); batchSetQuery.Fields = FieldValue.AllFields; batchSetQuery.Condition = new WholeNumberCondition(ArtifactQueryFieldNames.ArtifactID, NumericConditionEnum.EqualTo, newID); QueryResultSet<kCura.Relativity.Client.DTOs.BatchSet> queryResultSet = null; try { queryResultSet = proxy.Repositories.BatchSet.Query(batchSetQuery); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred reading the batch set after updating: {0}", ex.Message)); return false; } kCura.Relativity.Client.DTOs.BatchSet readBatchSet2 = queryResultSet.Results [0].Artifact; Console.WriteLine("Updated value of Name: " + readBatchSet2.Name); Console.WriteLine("Updated value of AutoCreateRate: " + readBatchSet2.AutoCreateRateMinutes); return true; } 6.2.2 Creating and deleting a BatchSet This code sample illustrates how to call the Create() and Delete() methods on a BatchSet repository. public static bool Create_then_Delete_A_BatchSet_Using_Repositories (IRSAPIClient proxy) { // STEP 1: Create a BatchSet DTO and set its properties. DTOs.BatchSet newBatchSet = new DTOs.BatchSet(); newBatchSet.Name = "My Batch Set"; Relativity | Services API Guide - 88 newBatchSet.BatchPrefix = "Batch"; newBatchSet.MaximumBatchSize = 5; newBatchSet.BatchDataSource = new DTOs.Artifact(1036604); // STEP 2: Declare a variable to hold the returned values. WriteResultSet<DTOs.BatchSet> results = new WriteResultSet<DTOs.BatchSet>(); Int32 newID = new Int32(); // STEP 3: Call the Services API to create the BatchSet. try { results = proxy.Repositories.BatchSet.Create(newBatchSet); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } if (results.Success) { newID = results.Results[0].Artifact.ArtifactID; Console.WriteLine(string.Format("ID of the new Batch Set: {0}", newID)); // STEP 4: Instantiate BatchSet with the ID of the BatchSet that you want to delete. DTOs.BatchSet batchSetToDelete = new DTOs.BatchSet(newID); // STEP 5: Call the Services API to delete the BatchSet just created. try { results = proxy.Repositories.BatchSet.Delete(batchSetToDelete); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } Console.WriteLine(string.Format("Overall Status of Deleting a Batch Set: {0}", results.Success)); } return true; } Relativity | Services API Guide - 89 6.2.3 Creating Batches for a BatchSet You can use the CreateBatchesAsync() method to add Batch objects to a BatchSet as illustrated in this code sample. For more information about this method, see Asynchronous framework on page 78. public static Boolean Create_Batches_For_BatchSet(IRSAPIClient proxy) { // STEP 1: Create a new BatchSet object. DTOs.BatchSet batchSet = new DTOs.BatchSet(); batchSet.Name = "My Batch Set"; batchSet.MaximumBatchSize = 5; batchSet.BatchPrefix = "APISAMPLES-"; batchSet.BatchDataSource = new DTOs.Artifact(1036604); // STEP 2: Call the Services API to create the BatchSet. WriteResultSet<DTOs.BatchSet> createResults; try { createResults = proxy.Repositories.BatchSet.Create(batchSet); } catch (Exception ex) { Console.WriteLine(String.Format("An error occurred: {0}", ex.Message)); return false; } if (!createResults.Success) { Console.WriteLine(String.Format("An error occurred: {0}", createResults.Message)); return false; } // STEP 3: Add Handlers for events that may occur while creating Batches. proxy.ProcessProgress += HandleProcessProgressEvent; proxy.ProcessComplete += HandleProcessCompleteEvent; proxy.ProcessFailure += HandleProcessFailureEvent; proxy.ProcessCancelled += HandleProcessCancelEvent; // STEP 4: Create Batches for the Batch Set. While the server is executing this call, // your event handlers are called with progress and completion events. Int32 batchSetArtifactID = createResults.Results[0].Artifact.ArtifactID; ProcessOperationResult processResult; try Relativity | Services API Guide - 90 { processResult = proxy.Repositories.BatchSet.CreateBatchesAsync(new DTOs.BatchSet(batchSetArtifactID)); } catch (Exception ex) { Console.WriteLine(String.Format("An error occurred: {0}", ex.Message)); return false; } if (!processResult.Success) { Console.WriteLine(String.Format("An error occurred: {0}", processResult.Message)); return false; } return true; } 6.2.4 Querying for a Batch Set To query for a BatchSet, you can use the fields listed in the following table. For more information, see Querying on page 227 and Creating, updating, and querying BatchSet objects on page 85. Artifact ID Artifact ID Auto Create Rate (minutes) Batch Data Source Batch Prefix Created By Created On Documents to be Batched Family Field Last Error Reported Fields for BatchSet queries Last Modified By Last Modified On Last Successful Run Maximum Batch Size Minimum Batch Size Name Reviewed Security Status 6.2.5 Canceling the creation of a BatchSet When you cancel the creation of a BatchSet, Relativity stops creating new Batches but it doesn't delete those that were already created. This code sample illustrates how to use the FlagProcessForCancellationAsync() to cancel Batch creation. For more information about this method, see Asynchronous framework on page 78. public static Boolean Cancel_Batch_Creation(IRSAPIClient proxy) { Relativity | Services API Guide - 91 // STEP 1: Create a new BatchSet object. DTOs.BatchSet batchSet = new DTOs.BatchSet(); batchSet.Name = "My Batch Set"; batchSet.MaximumBatchSize = 5; batchSet.BatchPrefix = "APISAMPLES-"; batchSet.BatchDataSource = new DTOs.Artifact(1036604); // STEP 2: Call the Services API to create the BatchSet. WriteResultSet<DTOs.BatchSet> createResults; try { createResults = proxy.Repositories.BatchSet.Create(batchSet); } catch (Exception ex) { Console.WriteLine(String.Format("An error occurred: {0}", ex.Message)); return false; } if (!createResults.Success) { Console.WriteLine(String.Format("An error occurred: {0}", createResults.Message)); return false; } // STEP 3: Add Handlers for events that may occur while creating batches. proxy.ProcessProgress += HandleProcessProgressEvent; proxy.ProcessComplete += HandleProcessCompleteEvent; proxy.ProcessFailure += HandleProcessFailureEvent; proxy.ProcessCancelled += HandleProcessCancelEvent; // STEP 4: Create batches for the Batch Set. While the server is executing this call, // your event handlers will be called with progress and completion events. Int32 batchSetArtifactID = createResults.Results[0].Artifact.ArtifactID; ProcessOperationResult processResult; try { processResult = proxy.Repositories.BatchSet.CreateBatchesAsync(new DTOs.BatchSet(batchSetArtifactID)); } catch (Exception ex) { Relativity | Services API Guide - 92 Console.WriteLine(String.Format("An error occurred: {0}", ex.Message)); return false; } if (!processResult.Success) { Console.WriteLine(String.Format("An error occurred: {0}", processResult.Message)); return false; } // STEP 5: Cancel batch creation. After executing this call, the Relativity server // stops creating Batches, but it doesn't delete the Batches that were already created. System.Guid processID = processResult.ProcessID; try { processResult = proxy.FlagProcessForCancellationAsync(proxy.APIOptions, processID); } catch (Exception ex) { Console.WriteLine(String.Format("An error occurred cancelling batch set creation: {0}", ex.Message)); return false; } if (!processResult.Success) { Console.WriteLine(String.Format("An error occurred: {0}", processResult.Message)); return false; } return true; } 6.2.6 Purging Batches for a BatchSet You can use the PurgeBatchesAsync() method to delete the Batch objects associated with a BatchSet object as illustrated in this code sample. For more information about this method, see Asynchronous framework on page 78. public static Boolean Purge_Batches_For_BatchSet(IRSAPIClient proxy) Relativity | Services API Guide - 93 { // STEP 1: Add Handlers for events that may occur while creating batches. proxy.ProcessProgress += HandleProcessProgressEvent; proxy.ProcessComplete += HandleProcessCompleteEvent; proxy.ProcessFailure += HandleProcessFailureEvent; proxy.ProcessCancelled += HandleProcessCancelEvent; // STEP 2: Call the server method to delete the existing batches for a particular batch set. Int32 batchSetArtifactID = 1036606; ProcessOperationResult processResult; try { processResult = proxy.Repositories.BatchSet.PurgeBatchesAsync(new DTOs.BatchSet(batchSetArtifactID)); } catch (Exception ex) { Console.WriteLine(String.Format("An error occurred: {0}", ex.Message)); return false; } if (!processResult.Success) { Console.WriteLine(String.Format("An error occurred: {0}", processResult.Message)); return false; } return true; } 6.2.7 Event handlers used in code samples These event handlers are referenced in the previous code samples: n Used for progress notification while executing an asynchronous method. public static void HandleProcessProgressEvent(Object sender, ProcessProgressEventArgs eventArgs) { ProcessInformation processInformation = eventArgs.ProcessInformation; Console.WriteLine("For the Batch Set operation, the number of operations completed is " + processInformation.OperationsCompleted.ToString()); } Relativity | Services API Guide - 94 n Used for notification when an asynchronous method successfully finishes. public static void HandleProcessCompleteEvent(Object sender, ProcessCompleteEventArgs eventArgs) { ProcessInformation processInformation = eventArgs.ProcessInformation; Console.WriteLine("The Batch Set operation completed"); } n Used for notification when an asynchronous method finishes with an error. public static void HandleProcessFailureEvent(Object sender, ProcessFailureEventArgs eventArgs) { ProcessInformation processInformation = eventArgs.ProcessInformation; if (processInformation.Success == false) Console.WriteLine("There was a problem retrieving the process information, the message is " + processInformation.Message); else Console.WriteLine("The Batch Set operation failed, the process state is " + processInformation.State.ToString()); } n Used for notification when an asynchronous method stops due to a cancellation request. public static void HandleProcessCancelEvent(Object sender, ProcessCancelEventArgs eventArgs) { Console.WriteLine("The Batch Set operation was canceled"); } 6.3 Choice In Relativity, choices are the predetermined values that you apply to single and multi-choice list fields. For more information, see Choices in the Relativity 8.1 Documentation site. The Services API supports read and query operations on a Choice DTO. 6.3.1 Reading a Choice To read Field values on a Choice, you can use the Read() method on the Choice repository as illustrated in this code sample. public static bool Read_Choices(IRSAPIClient proxy) { Relativity | Services API Guide - 95 // STEP 1: Create DTO with criteria for the read. DTOs.Choice choiceToRead = new DTOs.Choice(1016577); choiceToRead.Fields = FieldValue.AllFields; // STEP 2: Create ResultSet which contain Results from the read operation. ResultSet<DTOs.Choice> results = new ResultSet<DTOs.Choice>(); // STEP 3: Perform the read operation. try { results = proxy.Repositories.Choice.Read(choiceToRead); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } // Check for success. if (!results.Success) { Console.WriteLine("The Read operation was not successful.{0}{1} ",Environment.NewLine, results.Message); return false; } Result<DTOs.Choice> choiceDtoArtifact = results.Results[0]; // Output the results. SampleHelpers.APIHelpers.Print_All_Properties(choiceDtoArtifact.Artifact); return true; } 6.3.2 Querying on a Choice This code sample illustrates how to query for Choice DTOs created at the administrator level in Relativity. public static bool Query_for_Admin_Choices(IRSAPIClient proxy) { // STEP 1: Setup your query criteria. WholeNumberCondition criteria = new WholeNumberCondition("Choice Type ID", NumericConditionEnum.EqualTo, 1000036); Query<DTOs.Choice> query = new DTOs.Query<DTOs.Choice> { Condition = criteria }; Relativity | Services API Guide - 96 query.Fields = FieldValue.AllFields; // STEP 2: Create QueryResultSet to collect query results. QueryResultSet<DTOs.Choice> result = new QueryResultSet<DTOs.Choice>(); // STEP 3: Perform the query. try { result = proxy.Repositories.Choice.Query(query, 0); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } // Check for success. if (!result.Success) { Console.WriteLine("The Query operation was not successful.{0}{1} ",Environment.NewLine, result.Message); return false; } Console.WriteLine("Number of Choices returned: {0}", result.Results.Count); // Output the results. foreach (DTOs.Result<DTOs.Choice> choiceResult in result.Results) { Console.WriteLine("{0}Name:{1}", Environment.NewLine, choiceResult.Artifact.Name); Console.WriteLine("ArtifactID:{0}", choiceResult.Artifact.ArtifactID); } return true; } 6.4 Client In Relativity, clients are companies or other organizations associated with users and matters. For more information, see Clients in the the Relativity 8.1 Documentation site. The Services API supports all CRUD and query operations on a Client DTO. 6.4.1 Creating a Client To create a Client, you must set the properties on the DTO and call the Create() method on the Client repository as illustrate in this code sample. Relativity | Services API Guide - 97 public static bool Create_Client(IRSAPIClient proxy) { //STEP 1: Create a DTO by setting the necessary properties. DTOs.Client client = new DTOs.Client(); client.Name = "Sample Client"; client.ClientNumber = "Four more than Three"; client.Status = new DTOs.Choice(669); //STEP 2: Create a WriteResultSet. It provides details after the create operation. WriteResultSet<DTOs.Client> resultSet = new WriteResultSet<DTOs.Client>(); //STEP 3: Perform the create operation. try { resultSet = proxy.Repositories.Client.Create(client); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } //Check for success. if (!resultSet.Success) { Console.WriteLine("The Create operation failed.{0}{1} ",Environment.NewLine, resultSet.Message); return false; } //Output the results. Console.WriteLine("The Create succeeded."); DTOs.Client createdClient = resultSet.Results[0].Artifact; Console.WriteLine("{0}The Artifact of the New Client is: {1}", Environment.NewLine, createdClient.ArtifactID); return true; } 6.4.2 Reading a Client To read Field values on a Client, you can use the Read() method on the Client repository as illustrated in this code sample. Relativity | Services API Guide - 98 public static bool Read_Client(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = -1; //STEP 1: Create the Client DTO to read. kCura.Relativity.Client.DTOs.Client client = new kCura.Relativity.Client.DTOs.Client(1015461); client.Fields.Add(new FieldValue(ClientFieldNames.Name)); //STEP 2: Create ResultSet to collect information about the Read. ResultSet<kCura.Relativity.Client.DTOs.Client> resultSet = new ResultSet<kCura.Relativity.Client.DTOs.Client>(); //STEP 3: Perform the Read. try { resultSet = proxy.Repositories.Client.Read(client); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } //Check for success. if (!resultSet.Success) { Console.WriteLine("The Query operation failed.{0}{1}", Environment.NewLine, resultSet.Message); return false; } // Output the results. Console.WriteLine("Number of clients returned: {0}", resultSet.Results.Count); foreach (Result<kCura.Relativity.Client.DTOs.Client> clientResult in resultSet.Results) { Console.WriteLine("{0}Name:{1}", Environment.NewLine, clientResult.Artifact.Name); Console.WriteLine("ArtifactID:{0}", clientResult.Artifact.ArtifactID); } return true; } Relativity | Services API Guide - 99 6.4.3 Updating a Client To modify a Field on a Client, you can use the Update() method on the Client repository as illustrated in this code sample. public static bool Update_Client(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = -1; //STEP 1: Create the Client Artifact that you want to read. kCura.Relativity.Client.DTOs.Client client = new kCura.Relativity.Client.DTOs.Client(1015461); client.Fields.Add(new FieldValue(ClientFieldNames.Name)); //STEP 2: Create ResultSet to collect information about the Read. ResultSet<kCura.Relativity.Client.DTOs.Client> resultSet = new ResultSet<kCura.Relativity.Client.DTOs.Client>(); //STEP 3: Perform the Read. try { resultSet = proxy.Repositories.Client.Read(client); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } //Check for success. if (!resultSet.Success) { Console.WriteLine("The Query operation failed.{0}{1}", Environment.NewLine, resultSet.Message); return false; } //STEP 4: Get the Client Artifact and set the fields to be updated. kCura.Relativity.Client.DTOs.Client clientResult = resultSet.Results.FirstOrDefault().Artifact; clientResult.Name = "Updated Sample Client"; WriteResultSet<kCura.Relativity.Client.DTOs.Client> writeResultSet; //STEP 5: Update the Client Artifact. try Relativity | Services API Guide - 100 { writeResultSet = proxy.Repositories.Client.Update(clientResult); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } //Check for success. if (!writeResultSet.Success) { Console.WriteLine("The Update operation failed.{0}{1}", Environment.NewLine, writeResultSet.Message); return false; } return true; } 6.4.4 Deleting a Client You can delete a Client object by calling the Delete() method on the Client repository as illustrated in this code sample. public static bool Delete_Client(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = -1; //STEP 1: Create the Client artifact to delete. kCura.Relativity.Client.DTOs.Client client = new kCura.Relativity.Client.DTOs.Client(1015461); //STEP 2: Create ResultSet to collect information about the Delete. ResultSet<kCura.Relativity.Client.DTOs.Client> resultSet = new ResultSet<kCura.Relativity.Client.DTOs.Client>(); //STEP 3: Perform the Delete. try { resultSet = proxy.Repositories.Client.Delete(client); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; Relativity | Services API Guide - 101 } //Check for success. if (!resultSet.Success) { Console.WriteLine("The Query operation failed.{0}{1}", Environment.NewLine, resultSet.Message); return false; } return true; } 6.4.5 Querying for a Client This code sample illustrates how to set query conditions, call the Query() method on the Client repository, and iterate through the result set. For more information, see Querying on page 227. public static bool Query_Client(IRSAPIClient proxy) { //STEP 1: Create Query and ObjectsCondition(s), it will provide details after the Query Operation. WholeNumberCondition clientCondition = new WholeNumberCondition(ArtifactQueryFieldNames.ArtifactID, NumericConditionEnum.EqualTo, 1016204); Query<DTOs.Client> query = new Query<DTOs.Client> { Condition = clientCondition }; query.Fields = FieldValue.AllFields; //STEP 2: Create QueryResultSet to collect information about the DTO after Query. QueryResultSet<DTOs.Client> resultSet = new QueryResultSet<DTOs.Client>(); //STEP 3: Perform the Query. try { resultSet = proxy.Repositories.Client.Query(query, 0); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } //Check for success. Relativity | Services API Guide - 102 if (!resultSet.Success) { Console.WriteLine("The Query operation failed.{0}{1}", Environment.NewLine, resultSet.Message); return false; } // Output the results. Console.WriteLine("Number of clients returned: {0}", resultSet.Results.Count); foreach (Result<DTOs.Client> clientResult in resultSet.Results) { Console.WriteLine("{0}Name:{1}", Environment.NewLine, clientResult.Artifact.Name); Console.WriteLine("ArtifactID:{0}", clientResult.Artifact.ArtifactID); } return true; } 6.5 Document In Relativity, documents consist of the content for a review or other tasks, and they are stored in workspaces. For more information, see the Relativity 8.1 Documentation site. The Services API supports all CRUD and query operations on a Document DTO. 6.5.1 Fields on a Document DTO The Document DTO supports fields available on documents through the Relativity web UI as well as the following specialized fields: n n n n FolderName - represents the name of the folder in the Relativity, where the Document is located. HasImages - indicates whether images of the Document exist. HasNative - indicates whether a native file exists for the Document. RelativityImageCount - provides the number of images associated with a Document. 6.5.2 Creating a Document When you call the Create() method on a Document DTO, you can set only these properties: n n n RelativityNativeFileLocation – specifies the local path for the document native uploaded to Relativity, such as c:\myFolder\myDocument.docx. It is a required property that is used only by the Create() method. TextIdentifier – specifies the name of the document. It is a required property for the Create() method. See TextIdentifer property on DTOs on page 114. ParentArtifact – indicates the folder where the document is added. It is an optional property. Relativity | Services API Guide - 103 Only these fields are supported on the Create() method for Document. The values of any other fields won't be updated if you attempt to set them during a create operation. After the Create() method completes, you can use the Update() method to populate other fields on the Document object. Note: You can't update the native file through the Services API unless you delete and then recreate the document. However, you can use the Import API to update the native file. See Import API on the Relativity 8.1 Developers site. 6.5.2.1 Sample Code public static int Create_Document(IRSAPIClient proxy) { //STEP 1: Create the Document object, specifying an optional Parent Folder. The name of //the identifier field will change depending on the workspace, so use the TextIdentifier // property. DTOs.Document document = new DTOs.Document(); document.RelativityNativeFileLocation = "C:\\SourceCode\\Mainline\\EDDS\\kCura.Relativity.Client.APISamples \\SampleFiles\\LoremIpsum.docx"; document.TextIdentifier = "My New Document 001"; //STEP 2: The Parent Folder is specified by setting Parent Artifact ID. Int32 folderId = 1003697; //This step is optional. document.ParentArtifact = new DTOs.Artifact(folderId); WriteResultSet<DTOs.Document> createResults = new WriteResultSet<DTOs.Document>(); //STEP 3: Attempt to create the document. try { createResults = proxy.Repositories.Document.Create(document); } catch (Exception ex) { // If an error occurs while trying to create the document, write an error message. Console.WriteLine("Error: " + ex.Message); return -1; } //Check for success. if (createResults.Success) { Relativity | Services API Guide - 104 Console.WriteLine("Created a new document with ArtifactId = {0}", createResults.Results.First().Artifact.ArtifactID); return createResults.Results.First().Artifact.ArtifactID; } else { Console.WriteLine("Error creating the document: {0}", createResults.Message); return -1; } } 6.5.3 Reading a Document The Read() method doesn't return a native file for a document. You must use the Download() method if you want the original native file. However, you can retrieve extracted text with the Read() method. public static bool Read_Document(IRSAPIClient proxy) { // STEP 1: Create an object specifying the Artifact ID // and which fields to return. DTOs.Document document = new DTOs.Document(1035472); document.Fields = FieldValue.AllFields; // STEP 2: Call the read method on the Document Repository. ResultSet<DTOs.Document> results = new ResultSet<DTOs.Document>(); try { results = proxy.Repositories.Document.Read(document); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } if (!results.Success) { Console.WriteLine("Error: " + results.Message); return false; } // STEP 3: Get the Document artifact from the read results. DTOs.Document documentArtifact = results.Results.FirstOrDefault().Artifact; Relativity | Services API Guide - 105 Console.WriteLine("Document Control Number: " + documentArtifact.TextIdentifier); Console.WriteLine("Document Last Modified: " + documentArtifact.SystemLastModifiedOn); Console.WriteLine("Document Artifact ID: " + documentArtifact.ArtifactID); return true; } 6.5.4 Updating a Document You can modify properties of the Document by calling the Update() method as illustrated in this code sample. public static bool Update_Document(IRSAPIClient proxy) { // STEP 1: Create an object specifying the Artifact ID // and which fields to return. DTOs.Document document = new DTOs.Document(1035472); document.Fields = FieldValue.AllFields; // STEP 2: Read current values. ResultSet<DTOs.Document> results = new ResultSet<DTOs.Document>(); try { results = proxy.Repositories.Document.Read(document); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } if (!results.Success) { Console.WriteLine("Error: " + results.Message); return false; } // STEP 3: Get the Artifact from the read results. DTOs.Document docArtifact = results.Results[0].Artifact; Console.WriteLine("Document Artifact ID: " + docArtifact.ArtifactID); Console.WriteLine("Document Text Identifier: " + docArtifact.TextIdentifier); Relativity | Services API Guide - 106 DTOs.Artifact singleObject1 = (DTOs.Artifact)(docArtifact["Single Object"].Value); Console.WriteLine("Document Single Object: " + singleObject1.ArtifactID.ToString()); // STEP 4: Modify properties of the document. // Specify a new value for Text Identifier, a property of the Artifact. docArtifact.TextIdentifier = "John552"; // Specify a new value for Single Object, a custom field in Relativity. // Custom fields must be accessed using the Fields collection. // For a Single Object, we specify the ArtifactID. docArtifact["Single Object"].Value = 1036035; // STEP 5: Call the Update method on the Document Respository. WriteResultSet<DTOs.Document> writeResultSet = null; try { writeResultSet = proxy.Repositories.Document.Update(new List<DTOs.Document> { docArtifact }); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } if (writeResultSet.Success) { Console.WriteLine("Document updated successfully"); docArtifact = results.Results[0].Artifact; Console.WriteLine("Document Artifact ID: " + docArtifact.ArtifactID); Console.WriteLine("Document Text Identifier: " + docArtifact.TextIdentifier); int singleObjectID = (int)(docArtifact["Single Object"].Value); Console.WriteLine("Document Single Object: " + singleObjectID.ToString ()); } else { string message = writeResultSet.Message; Relativity | Services API Guide - 107 if (message == null && writeResultSet.Results.Count > 0 && !writeResultSet.Results[0].Success) message = writeResultSet.Results[0].Message; Console.WriteLine("Error: " + message); return false; } // STEP 6: Read back the updated Artifact. try { results = proxy.Repositories.Document.Read(document); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } if (!results.Success) { Console.WriteLine("Error: " + results.Message); return false; } // STEP 7: Get the updated property values from the read results. docArtifact = results.Results[0].Artifact; Console.WriteLine("Document Artifact ID: " + docArtifact.ArtifactID); Console.WriteLine("Document Text Identifier: " + docArtifact.TextIdentifier); DTOs.Artifact singleObject2 = (DTOs.Artifact)(docArtifact["Single Object"].Value); Console.WriteLine("Document Single Object: " + singleObject2.ArtifactID.ToString()); return true; } 6.5.5 Deleting a Document This code sample illustrates how to remove a document from Relativity by calling the Delete() method on the Document repository. public static bool Delete_Document(IRSAPIClient proxy) { Relativity | Services API Guide - 108 DTOs.Document doc = new DTOs.Document(1035605); WriteResultSet<DTOs.Document> writeResultSet = new WriteResultSet<DTOs.Document>(); try { writeResultSet = proxy.Repositories.Document.Delete(doc); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } if (!writeResultSet.Success) { string message = writeResultSet.Message; if (message == null && writeResultSet.Results.Count > 0 && !writeResultSet.Results[0].Success) message = writeResultSet.Results[0].Message; Console.WriteLine("Error: " + message); return false; } else { Console.WriteLine("Document updated successfully"); } return true; } 6.5.6 Querying for a Document The Services API doesn't support query conditions for the following fields on the Document DTO: n n n Batch_AssignedTo Batch_BatchSet Batch_Status Note: The Services API returns query results for the Batch_AssignedTo, Batch_BatchSet, and Batch_Status fields. n RelativityNativeFileLocation (Set only during a create operation.) Relativity | Services API Guide - 109 6.5.6.1 Sample Code This code sample illustrates how to set query conditions, call the Query() method on the Document repository, and iterate through the result set. public static bool Query_Document_by_Control_Number(IRSAPIClient proxy) { // STEP 1: Setup your query criteria. TextCondition criteria = new TextCondition("Control Number", TextConditionEnum.EqualTo, "AS000005"); Query<DTOs.Document> query = new Query<DTOs.Document> { Condition = criteria, RelationalField = new FieldValue("Group Identifier"), Fields = new List<FieldValue> { new FieldValue("Control Number") } }; // STEP 2: Call the Query() method in the Document repository. QueryResultSet<DTOs.Document> result = null; try { result = proxy.Repositories.Document.Query(query, 0); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } Console.WriteLine(string.Format("Number of documents returned: {0}", result.TotalCount)); Console.WriteLine(string.Format("Additional Pages of Query Results Available?: {0}", !string.IsNullOrEmpty(result.QueryToken))); // STEP 3: Iterate the returned Document result. foreach (DTOs.Result<DTOs.Document> docResult in result.Results) { DTOs.Document doc = docResult.Artifact; Console.WriteLine("Document Control Number: " + doc.TextIdentifier); Console.WriteLine("Document Artifact ID: " + doc.ArtifactID); } return true; } Relativity | Services API Guide - 110 6.5.7 Downloading a native file You can use the Download() method on the repository for the Document DTO to download a native file. You can download a native file from Relativity as Stream object, or specify an output path on disk. Note: Use the Download() method on the Document Repository class rather than this method on the FileTransferProxy class. This approach is recommended for downloading native files. See File field on page 135. 6.5.7.1 Sample Code public static Stream Download_Document_Native(IRSAPIClient proxy) { //STEP 1: Define the ArtifactID of the document which has a native file. DTOs.Document doc = new DTOs.Document(1035607); //STEP 2: Listen to the Failure event to get exceptions. proxy.Failure += FileTransferFailureHandler; //STEP 3: Call the DownloadNative() method. KeyValuePair<DownloadResponse, Stream> documentNativeResponse; try { documentNativeResponse = proxy.Repositories.Document.DownloadNative (doc); } catch (Exception ex) { //If an error occurs while trying to download the document, write an error message. Console.WriteLine("Error: " + ex.Message); return null; } //STEP 4: A 'null' Key and Value from the response indicates failure. if (documentNativeResponse.Key != null && documentNativeResponse.Value != null) { Console.WriteLine("Download succeeded: {0} bytes", documentNativeResponse.Value.Length); return documentNativeResponse.Value; } else { return null; } } Relativity | Services API Guide - 111 private static void FileTransferFailureHandler(FailureEventArgs eventArgs) { Console.WriteLine("The following error occurred during the download for the document with ArtifactId {0}: {1}", eventArgs.TargetField.ObjectArtifactId, eventArgs.Exception); } 6.6 Error The Services API provides an Error DTO, which represents an exception thrown in Relativity. The Services API supports only the create operation on an Error DTO. For more information, see Troubleshooting the Services API on page 236. 6.6.1 Writing to the error log You can write an entry to the Relativity error log using the Create() method on the RSAPIClient proxy. This method takes an ArtifactRequest parameter with the ArtifactTypeName set to Error or the ArtifactTypeID set to 18. The Fields collection provides the field values to set on the newly created Error object. The Message or Full Error field is required. When you call the Create() method, the value set in the Workspace on the APIOptions object affects the information added to the error log : n n n If Workspace is set to -1, the error log creates an error but not associate it with a specific workspace. If Workspace is set to valid ID for a workspace, the error log automatically creates an error associated with that workspace. If you pass a new field named Workspace with a valid ID for a workspace (overriding the field in the APIOptions object), the error log creates an error associated with the workspace specified by this field. The following sample code illustrates how to write errors to the Relativity error log. public static bool Create_Error_Message(IRSAPIClient proxy) { // STEP 1: Create an Error DTO. DTOs.Error errorToCreate = new DTOs.Error(); // STEP 2: Create a unexpected condition which throws an exception. try { throw new Exception("Invalid Data"); } catch (Exception ex) { errorToCreate.FullError = ex.StackTrace; errorToCreate.Message = ex.Message; } Relativity | Services API Guide - 112 // Set additional fields. The Workspace can be inferred from the APIOptions instance. errorToCreate.SendNotification = false; errorToCreate.Server = Environment.MachineName; errorToCreate.Source = "3rd Party Application"; errorToCreate.URL = "https://url/to/relativity/page.aspx"; errorToCreate.Workspace = new DTOs.Workspace(1016204); //STEP 3: Attempt to create the error. DTOs.WriteResultSet<DTOs.Error> resultSet = null; try { resultSet = proxy.Repositories.Error.Create(errorToCreate); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } //STEP 4: Check for success. if (resultSet.Success) { Console.WriteLine("Error Message successfully written to Relativity Error Log"); } else { Console.WriteLine("Error Message not written to Relativity Error Log"); return false; } return true; } 6.7 Field Relativity uses Fields to store document and other metadata, as well as coding selections made by a reviewer. It provides multiple Field types to support this functionality, which are also available through the Services API. 6.7.1 System type fields System type fields are properties on the base Artifact class. Each DTO type has the following consistent set of base properties returned on the Artifact: Relativity | Services API Guide - 113 Base Artifact Properties in the DTO Layer ArtifactID ArtifactTypeID ArtifactTypeName SystemCreatedBy SystemCreatedOn ArtifactTypeGuids SystemLastModifiedOn SystemLastModifiedBy Guids ParentArtifact 6.7.1.1 TextIdentifer property on DTOs Except for the Error DTO, all other DTOs have a TextIdentifier property that displays the Identifier field of an object. For example, TextIdentifier equals the Name field on instances of Relativity Dynamic Objects RDOs. You can use it in place of the Name property on RDOs. If you set both the TextIdentifier and Name properties during object creation, their values must be equal or you receive an error message. Use the constant kCura.Relativity.Client.Constants.RELATIVITY_TEXT_IDENTIFIER to reference the Relativity Text Identifier field. 6.7.1.2 Using system type fields Since system type Fields don't have IDs, you must request them by name. Certain Fields have both a display name used in the Relativity web UI, and an SQL column name used to reference the Field in the database. For example, Field has a property with the display name Created By that contains a username as a String, and an SQL column name of CreatedByName. Another property on Field has no display name, but an SQL column name of CreatedBy, which returns the user’s ID. You can use either name when requesting Fields during a read or query operation. The Services API returns the Field using the name referenced in your request. If you don't specify any Fields in a request, the API returns all Fields using the display names when they exist and the SQL names when no display names are available. You can also set the StrictMode to maintain consistency on the Fields returned by the Services API, and field directives to indicate when you want all or no Fields returned. See StrictMode property and Field directives on page 73. 6.7.2 Field types The Services API supports several Field types that you can use on DTOs, including fixed-length text, single and multiple choice, single and multiple object, and others. 6.7.2.1 FixedLengthText and LongText Fields In Relativity, a FixedLengthText field is larger than 4,999 characters, while a fixed length fields is less than or equal to this number of characters. For DTOs, text fields are set and returned as Strings. View the code for creating an RDO and setting the value of a FixedLengthText field DTOs.RDO obj = new DTOs.RDO(); Relativity | Services API Guide - 114 obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.FIXED_LENGTH_FIELD, "Field Value")); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Create(obj); View the code for reading a FixedLengthText field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.FIXED_LENGTH_FIELD)); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Read(obj); DTOs.RDO resultsObject = results.Results[0].Artifact; string fieldValue = resultsObject[GUIDS.FIXED_LENGTH_ FIELD].ValueAsFixedLengthText; View the code for updating a FixedLengthText field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.FIXED_LENGTH_FIELD, "Updated Field Value")); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Update(obj); View the code for querying on RDOs with a value in their FixedLengthText fields DTOs.Query<DTOs.RDO> query = new DTOs.Query<DTOs.RDO>(); query.ArtifactTypeGuid = GUIDS.OBJECT_ARTIFACT_TYPE_GUID; query.Fields = DTOs.FieldValue.AllFields; query.Condition = new TextCondition(GUIDS.FIXED_LENGTH_FIELD, TextConditionEnum.EqualTo, "Field Value"); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Query(query); 6.7.2.2 SingleChoice Fields In Relativity, a SingleChoice field has a predetermined set of values called choices. A user can only select one of these choices for coding or other purposes. Use these guidelines for SingleChoice fields: n Create or update SingleChoice fields on documents or RDOs. Set the FieldValue to a Choice DTO instantiated with the Artifact ID or Artifact GUID. You can't set the values on SingleChoice fields to a text Relativity | Services API Guide - 115 n representation of a Choice. For example, you can set the value of the Field Single Choice Field on a Document or RDO to a Choice using its ArtifactID (such as 100456) or its GUID. Retrieve a list of valid Choices for a SingleChoice field as follows: o o Perform a read operation using the Artifact ID or GUID for Single Choice Field and the ArtifactTypeName of Field (or ArtifactTypeID = 14). Request that the Choices field is returned. A List of kCura.Relativity.Client.DTOs.Choice is returned as illustrated in the following code. DTOs.Field choiceFieldToRead = new DTOs.Field(1038781); choiceFieldToRead.Fields.Add(new DTOs.FieldValue (DTOs.FieldFieldNames.Choices)); var readResults = proxy.Repositories.Field.Read(choiceFieldToRead); DTOs.MultiChoiceFieldValueList choices = readResults.Results[0].Artifact [DTOs.FieldFieldNames.Choices].ValueAsMultipleChoice; n Read operations on a SingleChoice field return the following data: o o o o Type of the Value returned using the ValueAsSingleChoice is kCura.Relativity.Choice.DTOs Text value of the Choice set on the Field is the Name property of the Choice object ArtifactID property of the Choice object is the ArtifactID of the Choice definition within the Workspace. ArtifactGuids property is a List of GUIDs assigned to Choice definition. View sample code for creating an RDO and setting the value of a SingleChoice field DTOs.RDO obj = new DTOs.RDO(); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); DTOs.Choice choice = new DTOs.Choice(GUIDS.SINGLE_CHOICE_1); obj.Fields.Add(new DTOs.FieldValue(GUIDS.SINGLE_CHOICE_FIELD, choice)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Create(obj); View sample code for reading a SingleChoice field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.SINGLE_CHOICE_FIELD)); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Read(obj); DTOs.RDO resultsObject = results.Results[0].Artifact; DTOs.Choice choiceValue = resultsObject[GUIDS.SINGLE_CHOICE_ FIELD].ValueAsSingleChoice; Relativity | Services API Guide - 116 View sample code for updating a SingleChoice field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); DTOs.Choice choice = new DTOs.Choice(GUIDS.SINGLE_CHOICE_1); obj.Fields.Add(new DTOs.FieldValue(GUIDS.SINGLE_CHOICE_FIELD, choice)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Update(obj); View sample code for querying for RDOs with a value in their SingleChoice fields DTOs.Query<DTOs.RDO> query = new DTOs.Query<DTOs.RDO>(); query.ArtifactTypeGuid = GUIDS.OBJECT_ARTIFACT_TYPE_GUID; query.Fields = DTOs.FieldValue.AllFields; query.Condition = new SingleChoiceCondition(GUIDS.SINGLE_CHOICE_FIELD, SingleChoiceConditionEnum.AnyOfThese, new Int32[] { ARTIFACTID.SINGLE_ CHOICE_1 }); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Query(query); 6.7.2.3 File Fields You use a File type field to upload a file for a non-document object. When you add a File field, Relativity automatically creates other fields containing file metadata, such as File Size, File Icon, and Text fields. For more information and code samples, see File field on page 135. 6.7.2.4 MultiChoice Fields In Relativity, a MultiChoice field has a predetermined set of values called choices. A user can select several choices for coding or other purposes. Use these guidelines for MultiChoice fields: n n Create and update operations require you to set the values of a MultiChoice Field by defining a MultiChoiceFieldValueList, which is used to set and return MultiChoice Fields on DTOs. When you have a .NET List of Choice DTOs (FieldValueList<DTOs.Choice>), the UpdateBehavior property indicates how the IDs in the FieldValueList are applied to the field (Replace or Merge). The default behavior is Replace, when no value is specified. If necessary, you can modified this list directly and pass it back in an update operation. You can populate this list from a read operation, or manually set it using a constructor that takes a List<DTOs.Choice> or parameter array of DTOs.Choice. Read or query operations return a MultiChoice value as a FieldValueList<DTOs.Choice> using the ValueAsMultipleChoice property. View sample code for creating an RDO and setting the value of a MultiChoice field DTOs.RDO obj = new DTOs.RDO(); Relativity | Services API Guide - 117 obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); DTOs.MultiChoiceFieldValueList choices = new DTOs.MultiChoiceFieldValueList(); choices.Add(new DTOs.Choice(GUIDS.MULTI_CHOICE_1)); choices.Add(new DTOs.Choice(GUIDS.MULTI_CHOICE_2)); obj.Fields.Add(new DTOs.FieldValue(GUIDS.MULTI_CHOICE_FIELD, choices)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Create(obj); View sample code for reading a MultiChoice field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.MULTI_CHOICE_FIELD)); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Read(obj); DTOs.RDO resultsObject = results.Results[0].Artifact; DTOs.FieldValueList<DTOs.Choice> fieldValue = (DTOs.FieldValueList<DTOs.Choice>) resultsObject[GUIDS.MULTI_CHOICE_ FIELD].ValueAsMultipleChoice; View sample code for updating a MultiChoice field on an RDO This sample also illustrates how to set the behavior of the MultiChoice Update to Replace, which modifies the existing value. DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); DTOs.MultiChoiceFieldValueList choices = new DTOs.MultiChoiceFieldValueList(); choices.Add(new DTOs.Choice(GUIDS.MULTI_CHOICE_1)); choices.Add(new DTOs.Choice(GUIDS.MULTI_CHOICE_2)); choices.UpdateBehavior = MultiChoiceUpdateBehavior.Replace; obj.Fields.Add(new DTOs.FieldValue(GUIDS.MULTI_CHOICE_FIELD, choices)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Update(obj); View sample code for querying for RDOs with values in their MultiChoice fields Relativity | Services API Guide - 118 DTOs.Query<DTOs.RDO> query = new DTOs.Query<DTOs.RDO>(); query.ArtifactTypeGuid = GUIDS.OBJECT_ARTIFACT_TYPE_GUID; query.Fields = DTOs.FieldValue.AllFields; query.Condition = new MultiChoiceCondition(GUIDS.MULTI_CHOICE_FIELD, MultiChoiceConditionEnum.AnyOfThese, new Int32[] { ARTIFACTID.MULTI_CHOICE_1, ARTIFACTID.MULTI_CHOICE_2 }); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Query(query); 6.7.2.5 SingleObject Fields In Relativity, a SingleObject field defines a one-to-many relationship between two objects. Use these guidelines for SingleObject fields: n n Create and update operations on a SingleObject field require you to pass DTO with the ArtifactID property set. On read and query operations, the ValueAsSingleObject property returns a SingleObject as a DTO with the type of the object that you specified in the operation. On these DTOs, only the ArtifactID on the base Artifact is populated, while the Fields collections is empty. Note: If the DTO layer doesn't support the requested object type, then Artifact is used as the return type. View sample code for creating an RDO and setting the value of a SingleObject field DTOs.RDO obj = new DTOs.RDO(); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); DTOs.Artifact fieldObj = new DTOs.Artifact(ARTIFACTID.FAKE_OBJECT_1); obj.Fields.Add(new DTOs.FieldValue(GUIDS.SINGLE_OBJECT_FIELD, fieldObj)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Create(obj); View sample code for reading a SingleObject field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.SINGLE_OBJECT_FIELD)); DTOs.ResultSet<DTOs.RDO> results = new DTOs.ResultSet<DTOs.RDO>(); results = client.Repositories.RDO.Read(obj); DTOs.RDO resultsObject = results.Results[0].Artifact; DTOs.Artifact artifactValue = resultsObject[GUIDS.SINGLE_OBJECT_ FIELD].ValueAsSingleObject; Relativity | Services API Guide - 119 View sample code for updating a SingleObject field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); DTOs.Artifact fieldObj = new DTOs.Artifact(ARTIFACTID.FAKE_OBJECT_1); obj.Fields.Add(new DTOs.FieldValue(GUIDS.SINGLE_OBJECT_FIELD, fieldObj)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Update(obj); View sample code for querying on RDOs with values in their SingleObject fields DTOs.Query<DTOs.RDO> query = new DTOs.Query<DTOs.RDO>(); query.ArtifactTypeGuid = GUIDS.OBJECT_ARTIFACT_TYPE_GUID; query.Fields = DTOs.FieldValue.AllFields; query.Condition = new ObjectCondition(GUIDS.SINGLE_OBJECT_FIELD, ObjectConditionEnum.AnyOfThese, new Int32[] { ARTIFACTID.FAKE_OBJECT_1 }); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Query(query); 6.7.2.6 MultiObject Fields In Relativity, MultiObject fields define a many-to-many relationship between two objects. Use these guidelines for MultiObject fields: n n Create and update operations on MultiObject fields require you to pass a FieldValueList containing DTO objects with their ArtifactID property set. Read and query operations return a FieldValueList of the DTO for the object, using the GetValueAsMultipleObject<T>() method, where T is the type of Object. On these DTOs, only the ArtifactID on the base Artifact is populated, while the Fields collections is empty. Note: If the DTO layer doesn't support the requested object type, then Artifact is used as the return type. View sample code for creating an RDO and setting the value of a MultiObject field DTOs.RDO obj = new DTOs.RDO(); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); DTOs.FieldValueList<DTOs.Artifact> objects = new DTOs.FieldValueList<DTOs.Artifact>(); objects.Add(new DTOs.Artifact(ARTIFACTID.FAKE_OBJECT_1)); objects.Add(new DTOs.Artifact(ARTIFACTID.FAKE_OBJECT_2)); obj.Fields.Add(new DTOs.FieldValue(GUIDS.MULTI_OBJECT_FIELD, objects)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Create(obj); View sample code for reading a MultiObject field on an RDO Relativity | Services API Guide - 120 DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.MULTI_OBJECT_FIELD)); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Read(obj); DTOs.RDO resultsObject = results.Results[0].Artifact; DTOs.FieldValueList<DTOs.Artifact> fieldValue = resultsObject[GUIDS.MULTI_OBJECT_FIELD].GetValueAsMultipleObject<DTOs.Artifact>(); View sample code for updating a MultiObject field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); DTOs.FieldValueList<DTOs.Artifact> objects = new DTOs.FieldValueList<DTOs.Artifact>(); objects.Add(new DTOs.Artifact(ARTIFACTID.FAKE_OBJECT_1)); objects.Add(new DTOs.Artifact(ARTIFACTID.FAKE_OBJECT_2)); obj.Fields.Add(new DTOs.FieldValue(GUIDS.MULTI_OBJECT_FIELD, objects)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Update(obj); View sample code for querying on RDOs with values in their MultiObject fields DTOs.Query<DTOs.RDO> query = new DTOs.Query<DTOs.RDO>(); query.ArtifactTypeGuid = GUIDS.OBJECT_ARTIFACT_TYPE_GUID; query.Fields = DTOs.FieldValue.AllFields; query.Condition = new ObjectsCondition(GUIDS.MULTI_OBJECT_FIELD, ObjectsConditionEnum.AnyOfThese, new Int32[] { ARTIFACTID.FAKE_OBJECT_1, ARTIFACTID.FAKE_OBJECT_2 }); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Query(query); 6.7.2.7 User Fields In Relativity, a User field contains the rights that a user has to the current workspace. Use these guidelines for User fields: n n Create and update operations require you to set the User field to a User DTO with the ArtifactID property populated. On read and query operations, the ValueAsUser property returns the Value property of the User field as a type kCura.Relativity.Client.DTOs.User, which is populated with the ArtifactID and Name properties. View sample code for creating an RDO and setting the value of a User field Relativity | Services API Guide - 121 DTOs.RDO obj = new DTOs.RDO(); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); DTOs.User user = new DTOs.User(ARTIFACTID.USER); obj.Fields.Add(new DTOs.FieldValue(GUIDS.USER_FIELD, user)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Create(obj); View sample code for reading a User field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.USER_FIELD)); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Read(obj); DTOs.RDO resultsObject = results.Results[0].Artifact; DTOs.User userValue = resultsObject[GUIDS.USER_FIELD].ValueAsUser; View sample code for updating a User field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); DTOs.User user = new DTOs.User(ARTIFACTID.USER); obj.Fields.Add(new DTOs.FieldValue(GUIDS.USER_FIELD, user)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Update(obj); View sample code for querying on RDOs with values in their User fields DTOs.Query<DTOs.RDO> query = new DTOs.Query<DTOs.RDO>(); query.ArtifactTypeGuid = GUIDS.OBJECT_ARTIFACT_TYPE_GUID; query.Fields = DTOs.FieldValue.AllFields; query.Condition = new UserCondition(GUIDS.USER_FIELD, UserConditionEnum.EqualTo, "User Name"); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Query(query); 6.7.2.8 WholeNumber Fields Relativity uses WholeNumber fields to store any natural numbers. Use these guidelines for WholeNumber fields Relativity | Services API Guide - 122 n n Create and update operations require you to set the WholeNumber field to a whole number value. Read and query operations on a WholeNumber field return the Value property as an integer using the ValueAsWholeNumber property. View sample code for creating an RDO and setting the value of a WholeNumber field DTOs.RDO obj = new DTOs.RDO(); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); int wholeNumber = 100; obj.Fields.Add(new DTOs.FieldValue(GUIDS.WHOLE_NUMBER_FIELD, wholeNumber)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Create(obj); View sample code for reading a WholeNumber field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.WHOLE_NUMBER_FIELD)); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Read(obj); DTOs.RDO resultsObject = results.Results[0].Artifact; int? fieldValue = resultsObject[GUIDS.WHOLE_NUMBER_FIELD].ValueAsWholeNumber; View sample code for updating a WholeNumber field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); int wholeNumber = 5; obj.Fields.Add(new DTOs.FieldValue(GUIDS.WHOLE_NUMBER_FIELD, wholeNumber)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Update(obj); View sample code for querying on RDOs with values in their WholeNumber fields DTOs.Query<DTOs.RDO> query = new DTOs.Query<DTOs.RDO>(); query.ArtifactTypeGuid = GUIDS.OBJECT_ARTIFACT_TYPE_GUID; query.Fields = DTOs.FieldValue.AllFields; int queryWholeNumber = 250; query.Condition = new WholeNumberCondition(GUIDS.WHOLE_NUMBER_FIELD, NumericConditionEnum.GreaterThan, queryWholeNumber); Relativity | Services API Guide - 123 DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Query(query); 6.7.2.9 Decimal Fields In Relativity, Decimal fields store numeric values that may include decimals. Use these guidelines for Decimal fields: n n Create and update operations require you to set the Decimal field to a decimal value. Read and query operations on a Decimal field return the Value property of the field as a decimal using the ValueAsDecimal property. View sample code for creating an RDO and setting the value of a Decimal field DTOs.RDO obj = new DTOs.RDO(); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); var decimalNumber = 100.25; obj.Fields.Add(new DTOs.FieldValue(GUIDS.DECIMAL_FIELD, decimalNumber)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Create(obj); View sample code for reading a Decimal field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.DECIMAL_FIELD)); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Read(obj); DTOs.RDO resultsObject = results.Results[0].Artifact; decimal? fieldValue = resultsObject[GUIDS.DECIMAL_FIELD].ValueAsDecimal; View sample code for updating a Decimal field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); decimal decimalNumber = 5.0M; obj.Fields.Add(new DTOs.FieldValue(GUIDS.DECIMAL_FIELD, decimalNumber)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Update(obj); View sample code for querying on RDOs with values in their Decimal fields Relativity | Services API Guide - 124 DTOs.Query<DTOs.RDO> query = new DTOs.Query<DTOs.RDO>(); query.ArtifactTypeGuid = GUIDS.OBJECT_ARTIFACT_TYPE_GUID; query.Fields = DTOs.FieldValue.AllFields; decimal queryDecimal = 50.0M; query.Condition = new DecimalCondition(GUIDS.DECIMAL_FIELD, NumericConditionEnum.GreaterThan, new Decimal[] { queryDecimal }); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Query(query); 6.7.2.10 Currency Fields Relativity uses Currency fields to store numeric values in a currency format. Use these guidelines for Currency fields: n n Create and update operations require you to set the Currency field to a decimal value. Read and query operations on a Currency field return the Value property of the field decimal using the ValueAsCurrency property. View sample code for creating an RDO and setting the value of a Currency field DTOs.RDO obj = new DTOs.RDO(); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); var currencyValue = 100.00M; obj.Fields.Add(new DTOs.FieldValue(GUIDS.CURRENCY_FIELD, currencyValue)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Create(obj); View sample code for reading a Currency field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.CURRENCY_FIELD)); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Read(obj); DTOs.RDO resultsObject = results.Results[0].Artifact; decimal? fieldValue = resultsObject[GUIDS.CURRENCY_FIELD].ValueAsCurrency; View sample code for updating a Currency field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); Relativity | Services API Guide - 125 var currencyValue = 5.00; obj.Fields.Add(new DTOs.FieldValue(GUIDS.CURRENCY_FIELD, currencyValue)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Update(obj); View sample code for querying on RDOs with values in their Currency fields DTOs.Query<DTOs.RDO> query = new DTOs.Query<DTOs.RDO>(); query.ArtifactTypeGuid = GUIDS.OBJECT_ARTIFACT_TYPE_GUID; query.Fields = DTOs.FieldValue.AllFields; decimal currencyValue = 100.00M; query.Condition = new DecimalCondition(GUIDS.CURRENCY_FIELD, NumericConditionEnum.GreaterThan, new Decimal[] { currencyValue }); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Query(query); 6.7.2.11 Date Fields Relativity uses Date fields to store the date or date and time. Use these guidelines for Date fields: n n Create and update operations require you to set the Date field to a .NET DateTime value. Read and query operations on a Date field return the Value property of the field as a .NET DateTime using the ValueAsDate property. View sample code for creating an RDO and setting the value of a Date field DTOs.RDO obj = new DTOs.RDO(); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.DATE_FIELD, DateTime.Now)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Create(obj); View sample code for reading a Date field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.DATE_FIELD, DateTime.Now)); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Read(obj); DTOs.RDO resultsObject = results.Results[0].Artifact; DateTime? fieldValue = resultsObject[GUIDS.DATE_FIELD].ValueAsDate; View sample code for updating a Date field on an RDO Relativity | Services API Guide - 126 DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.DATE_FIELD, DateTime.Now)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Update(obj); View sample code for querying on RDOs with values in their Date fields DTOs.Query<DTOs.RDO> query = new DTOs.Query<DTOs.RDO>(); query.ArtifactTypeGuid = GUIDS.OBJECT_ARTIFACT_TYPE_GUID; query.Fields = DTOs.FieldValue.AllFields; query.Condition = new DateTimeCondition(GUIDS.DATE_FIELD, DateTimeConditionEnum.GreaterThan, DateTime.Now); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Query(query); 6.7.2.12 Yes/No Fields In Relativity, a Yes/No field stores a Boolean value. Use these guidelines for Yes/No fields: n n Create and update operations require you to set the Yes/No field to a .NET Boolean value. Read and query operations on Yes/No field return the Value property of the field as a .NET Boolean using the ValueAsYesNo Property. View sample code for creating an RDO and setting the value of a Yes/No field DTOs.RDO obj = new DTOs.RDO(); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.YES_NO_FIELD, true)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Create(obj); View sample code for reading a Yes/No field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.YES_NO_FIELD)); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Read(obj); DTOs.RDO resultsObject = results.Results[0].Artifact; bool? fieldValue = resultsObject[GUIDS.YES_NO_FIELD].ValueAsYesNo; Relativity | Services API Guide - 127 View sample code for updating a Yes/No field on an RDO DTOs.RDO obj = new DTOs.RDO(artifactId); obj.ArtifactTypeGuids.Add(GUIDS.OBJECT_ARTIFACT_TYPE_GUID); obj.Fields.Add(new DTOs.FieldValue(GUIDS.YES_NO_FIELD, false)); DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.RDO.Update(obj); View sample code for querying on RDOs with values in their Yes/No fields DTOs.Query<DTOs.RDO> query = new DTOs.Query<DTOs.RDO>(); query.ArtifactTypeGuid = GUIDS.OBJECT_ARTIFACT_TYPE_GUID; query.Fields = DTOs.FieldValue.AllFields; query.Condition = new BooleanCondition(GUIDS.YES_NO_FIELD, BooleanConditionEnum.EqualTo, true); DTOs.ResultSet<DTOs.RDO> results = client.Repositories.RDO.Query(query); 6.7.3 Supported operations for Field types The following operations are supported for Field types on DTOs: Operation Create a Field on a DTO Read Field metadata Delete a Field (except system Field) from a DTO Query on Field metadata Supported for Fields on DTOs RDO and Document DTOs All DTOs All DTOs All DTOs Note: For information about updating DTOs, see DTO reference and code samples on page 81. When you update Documents and RDOs, the value of each Field on an object in the Fields collection is overwritten with a new value, except for MultiChoice Fields. See MultiChoice Fields on page 117. 6.7.4 Field code samples Relativity uses fields to store object information, document metadata, and coding choices. For more information, see Fields in the Relativity 8.1 Documentation site. The Services API supports create, read, delete, and query operations on a Field DTO. 6.7.4.1 Creating a Field You can use the Create() on the Field repository to add a new field to Relativity. The ObjectType indicates the object associated with this Field, such as Document as illustrated in this sample code. Relativity | Services API Guide - 128 public static bool Create_Field_Using_Repository(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = 1016204; // STEP 1: Create a Field DTO and populate the Fields. DTOs.Field dto = new DTOs.Field(); dto.Name = "This is a Single Choice field by GUID11"; dto.ObjectType = new DTOs.ObjectType() { DescriptorArtifactTypeID = (int) ArtifactType.Document }; // The ObjectType DTO can also be instantiated with the GUID of the // ObjectType instead of setting the DescriptorArtifactTypeID. // dto.ObjectType = new DTOs.ObjectType(new Guid("4D3BA67C-9F88-4D29-8E6C6DECB6A682D1")); dto.FieldTypeID = kCura.Relativity.Client.FieldType.SingleChoice; dto.IsRequired = false; dto.Unicode = false; dto.AvailableInFieldTree = false; dto.OpenToAssociations = false; dto.Linked = false; dto.AllowSortTally = true; dto.Wrapping = true; dto.AllowGroupBy = false; dto.AllowPivot = false; dto.IgnoreWarnings = true; dto.Width = ""; // STEP 2: Call the Create method on the repository and pass the DTO. WriteResultSet<DTOs.Field> resultSet; try { resultSet = proxy.Repositories.Field.Create(dto); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); return false; } if (!resultSet.Success) { Console.WriteLine("Error: " + resultSet.Message); foreach (Result<DTOs.Field> result in resultSet.Results) { Console.WriteLine("Result Error: " + result.Message); } return false; } Console.WriteLine("Overall status of creating a Field: " + resultSet.Success); Relativity | Services API Guide - 129 Console.WriteLine("New Artifact ID: " + resultSet.Results.FirstOrDefault ().Artifact.ArtifactID); return true; } 6.7.4.2 Creating a Relational Field In Relativity, a relational field is used to identify a group of related documents. You can upload your own icon for a relational field, which is displayed in the Related Items pane of the reviewer interface. For more information, see PaneIcon property on page 144. This code sample illustrates how to create a relational Field and set its pane icon. public void Create_RelationalField() { try { using (IRSAPIClient proxy = new RSAPIClient(new Uri("net.pipe://localhost/Relativity.Services"), new IntegratedAuthCredentials())) { proxy.APIOptions.WorkspaceID = WORKSPACE_ID; var fieldDTO = new kCura.Relativity.Client.DTOs.Field(); fieldDTO.Name = "Relational Field 1"; fieldDTO.FieldTypeID = (Int32)FieldType.FixedLengthText; fieldDTO.ObjectType = new kCura.Relativity.Client.DTOs.ObjectType() { DescriptorArtifactTypeID = (Int32)ArtifactType.Document }; fieldDTO.Length = 100; fieldDTO.IsRequired = false; fieldDTO.IncludeInTextIndex = false; fieldDTO.Unicode = false; fieldDTO.AllowHTML = false; fieldDTO.OpenToAssociations = false; fieldDTO.Linked = false; fieldDTO.AllowSortTally = false; fieldDTO.Width = "100"; fieldDTO.Wrapping = false; fieldDTO.AllowPivot = false; fieldDTO.AllowGroupBy = false; // Set IsRelational to true. fieldDTO.IsRelational = true; // After IsRelational is set to true, add other required fields. fieldDTO.FriendlyName = "New Relational Field Friendly Name"; Relativity | Services API Guide - 130 fieldDTO.Order = 100; fieldDTO.ImportBehavior = ImportBehavior.LeaveBlankValuesUnchanged; fieldDTO.RelationalView = null; //Null is the default "All Items" view. // The field "PaneIcon" takes a RelationalFieldIcon object that contains a path and a byte array // for the icon that represents the field at the bottom of the document review page. string path = "c:\\icon.png"; System.IO.FileStream ifStream = System.IO.File.OpenRead(path); Int32 len = (Int32)ifStream.Length; System.IO.BinaryReader reader = new System.IO.BinaryReader(ifStream); byte[] byteArr = new byte[len]; reader.Read(byteArr, 0, len); fieldDTO.PaneIcon = new RelationalFieldIcon(path, byteArr); WriteResultSet<kCura.Relativity.Client.DTOs.Field> writeResults = proxy.Repositories.Field.Create(fieldDTO); if (writeResults.Success) { Console.WriteLine(string.Format("Field with name {0} was created with Artifact ID {1}.", fieldDTO.Name, writeResults.Results [0].Artifact.ArtifactID)); } else { Console.WriteLine(string.Format("An error occurred creating field: {0}", writeResults.Message)); for (Int32 i = 0; i <= writeResults.Results.Count - 1; i++) { if (!writeResults.Results[i].Success) { Console.WriteLine(String.Format("An error occurred in create request {0}: {1}", i, writeResults.Results [i].Message)); foreach (string warning in writeResults.Results[0].WarningMessages) { Console.WriteLine("Warning: {0}", warning); } } Relativity | Services API Guide - 131 } } } } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); } } 6.7.4.3 Reading a Field To read values on a Field, you can use the Read() method on the Field repository as illustrated in this code sample. public static bool Read_Field_Using_Repository(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = 1016204; // STEP 1: Call the Read() method on the Field repository, passing a Field DTO. ResultSet<DTOs.Field> results; try { results = proxy.Repositories.Field.Read(new DTOs.Field(1035984) { Fields = FieldValue.AllFields }); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); return false; } if (!results.Success) { Console.WriteLine("Error: " + results.Message); foreach (Result<DTOs.Field> result in results.Results) { Console.WriteLine("Result Error: " + result.Message); } return false; } // STEP 2: Get the Field artifact from the read results. DTOs.Field fieldArtifact = results.Results.FirstOrDefault().Artifact; Console.WriteLine("Field Name: " + fieldArtifact.Name); Console.WriteLine("Field Type: " + fieldArtifact.FieldTypeID); Console.WriteLine("Object Type: " + fieldArtifact.ObjectType.DescriptorArtifactTypeID); Relativity | Services API Guide - 132 foreach (DTOs.Choice choice in fieldArtifact.Choices) Console.WriteLine("Valid Choice for Field = " + choice.Name + ":" + choice.ArtifactID ); return true; } 6.7.4.4 Deleting a Field You can use the Delete() method on the Field repository to remove fields as illustrated in this code sample. public static bool Delete_Field_Using_Repository(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = 1016204; // STEP 1: Define the Field to delete. kCura.Relativity.Client.DTOs.Field dto = new kCura.Relativity.Client.DTOs.Field(1039140); ResultSet<kCura.Relativity.Client.DTOs.Field> results; // STEP 2: Delete the Field. try { results = proxy.Repositories.Field.Delete(dto); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); return false; } if (!results.Success) { Console.WriteLine("Error: " + results.Message); foreach (Result<kCura.Relativity.Client.DTOs.Field> result in results.Results) { Console.WriteLine("Result Error: " + result.Message); } return false; } return true; } Relativity | Services API Guide - 133 6.7.4.5 Querying for a Field This code sample illustrates how to set query conditions, call the Query() method on the Field repository, and iterate through the result set. For more information, see Querying on page 227. public static bool Query_Field_Using_Repository(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = 1016204; // STEP 1: Define the query. Query<kCura.Relativity.Client.DTOs.Field> query = new Query<kCura.Relativity.Client.DTOs.Field>(); query.Fields = FieldValue.AllFields; query.Condition = new WholeNumberCondition(ArtifactQueryFieldNames.ArtifactID, NumericConditionEnum.EqualTo, 1039140); ResultSet<kCura.Relativity.Client.DTOs.Field> results; // STEP 2: Query for the Field. try { results = proxy.Repositories.Field.Query(query); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); return false; } if (!results.Success) { Console.WriteLine("Error: " + results.Message); foreach (Result<kCura.Relativity.Client.DTOs.Field> result in results.Results) { Console.WriteLine("Result Error: " + result.Message); } return false; } // STEP 3: Get the Field artifact from the read results. kCura.Relativity.Client.DTOs.Field fieldArtifact = results.Results.FirstOrDefault().Artifact; Console.WriteLine("Field Name: " + fieldArtifact.Name); Console.WriteLine("Field Type: " + fieldArtifact.FieldTypeID); Relativity | Services API Guide - 134 Console.WriteLine("Object Type: " + fieldArtifact.ObjectType.DescriptorArtifactTypeID); foreach (kCura.Relativity.Client.DTOs.Choice choice in fieldArtifact.Choices) Console.WriteLine("Valid Choice for Field = " + choice.Name + ":" + choice.ArtifactID); return true; } 6.7.5 File field A File type field is used to upload a file for a non-document object. When you add a File field, Relativity automatically creates other fields containing file metadata, such as File Size, File Icon, and Text fields. Note: The following sample code uses the variable called proxy to reference an instance of the RSAPIClient class that already has the APIOptions property set. For simplicity, this sample code doesn't check on completed operations or provide error handling. 6.7.5.1 Creating a File field on an RDO You can create a dynamic object and then add a File field on it. The following sections describe this two part process. Creating a Dynamic ObjectType Create a Dynamic ObjectType that will be referenced in other code samples illustrating File field usage. This Dynamic ObjectType will be called Our File Container. Create an ObjectType instance as follows: var objectType = new ObjectType(); Set the values for the appropriate fields: objectType.CopyInstancesOnWorkspaceCreation = false; objectType.Name = “Our File Container”; objectType.ParentArtifactTypeID = (int)ArtifactType.Case; objectType.Pivot = false; objectType.RelativityApplications = new FieldValueList<RelativityApplication> (); objectType.SnapshotAuditingEnabledOnDelete = true; Using the DTO layer, call the Create() method of the ObjectType repository: Relativity | Services API Guide - 135 var createResults = proxy.Repositories.ObjectType.Create(objectType); Save the value of the ArtifactID property for later use in file uploading: var ourFileContainerInstanceArtifactId = createResults.Results [0].Artifact.ArtifactID; To create an instance of the Dynamic ObjectType, you need to determine the DescriptorArtifactTypeID, which is a unique identifier for the newly created Dynamic Object type. Create a Query instance as follows: var query = new Query<ObjectType>(); Set the values for the appropriate fields. In general, you request only the fields that you need, rather than use the AllFields directive. query.Condition = new TextCondition(“Name”, TextConditionEnum.EqualTo, “Our File Container”); query.Fields = FieldValue.AllFields; Call the Query() method of the ObjectType repository: var queryResults = proxy.Repositories.ObjectType.Query(query); Save the value of the DescriptorArtifactTypeID property for later use in field creation. You might also want to confirm that the query executed successfully. var descriptorArtifactTypeId = queryResults.Results [0].Artifact.DescriptorArtifactTypeID; Creating a File field You can create a new File field on a Dynamic ObjectType. In the following sample code, the variable descriptorArtifactTypeId refers to the value retrieved from Creating a File field on an RDO on the previous page. Create a Field instance: var field = new kCura.Relativity.Client.DTOs.Field(); Set the values for the appropriate fields: Relativity | Services API Guide - 136 field.AllowGroupBy = false; field.AllowPivot = false; field.AllowSortTally = false; field.FieldTypeID = FieldType.File; field.IgnoreWarnings = true; field.IsRequired = false; field.Linked = false; field.Name = “File Field For Our File Container”; This ObjectType was added in Creating a File field on an RDO on page 135: field.ObjectType = new ObjectType { DescriptorArtifactTypeID = descriptorArtifactTypeId }; field.OpenToAssociations = false; field.Width = String.Empty; field.Wrapping = false; Call the Create() method of the Field repository: var createResults = proxy.Repositories.Field.Create(field); Save the value of the ArtifactID property for use in file uploading later: var fileFieldArtifactId = createResults.Results[0].Artifact.ArtifactID; 6.7.5.2 Uploading to a File field You can upload a file to a File field. You need to obtain the following identifiers to upload the file: n n ArtifactID of the File field associated with the object type ArtifactID of the object instance with a File field that needs to be populated Use this variable for the newly created File field: fileFieldArtifactId Use this variable for the newly created instance of our Dynamic Object type (Our File Container): ourFileContainerInstanceArtifactId Create an UploadRequest instance: var uploadRequest = new UploadRequest(proxy.APIOptions); Relativity | Services API Guide - 137 Set the values for the appropriate fields: uploadRequest.Metadata.FileName = @”C:\path\to\my\file.docx”; uploadRequest.Metadata.FileSize = new FileInfo (uploadRequest.Metadata.FileName).Length; This optional property indicates that the current contents in the File field will be overwritten: uploadRequest.Overwrite = true; uploadRequest.Target.FieldId = fileFieldArtifactId; uploadRequest.Target.ObjectArtifactId = ourFileContainerInstanceArtifactId; (Optional) Register an event handler for the UploadComplete event: proxy.UploadComplete += UploadCompleteHandler; Call the Upload() method on the RSAPIClient: proxy.Upload(uploadRequest); 6.7.5.3 Downloading from a File field You can download a file from a File field. The Services API provides an overloaded Download() method for this purpose: n n DownloadResponse Download(FileRequest fileRequest, string outputPath) - Downloads the file to the location specified by outputPath. KeyValuePair<DownloadResponse, Stream> Download(FileRequest fileRequest) - Downloads the file to the Stream of the KeyValuePair. In addition, you need to obtain the following identifiers to upload the file: n n ArtifactID of the File field associated with the object type ArtifactID of the object instance with a file that needs to be downloaded Use this variable for the ArtifactID of the newly created File field: fileFieldArtifactId Use this variable for the newly created instance of our Dynamic Object type (Our File Container): ourFileContainerInstanceArtifactId Create a FileRequest instance as follows: Relativity | Services API Guide - 138 var fileRequest = new FileRequest(proxy.APIOptions); fileRequest.Target.FieldId = fileFieldArtifactId; fileRequest.Target.ObjectArtifactId = ourFileContainerInstanceArtifactId; Register an event handler for the DownloadComplete event: proxy.DownloadComplete += DownloadCompleteHandler; Call one of the overload Download() methods on the RSAPIClient: proxy.Download(fileRequest, @”C:\path\to\downloaded\file.docx”); 6.7.5.4 Getting a download URL for a File field You can retrieve a URL from the File field on a Dynamic Object and then use the URL to download the associated file. The following code sample illustrates how to call the GetFileFieldDownloadURL() method on the proxy and download the required file. public static bool GetFileFieldDownloadURL(IRSAPIClient proxy) { // STEP 1: Set APIOptions and instantiate a new DownloadURLRequest with the APIOptions. proxy.APIOptions.WorkspaceID = 1015926; var downloadUrlRequest = new DownloadURLRequest(proxy.APIOptions); // STEP 2: Set properties in the DownloadURLRequest. downloadUrlRequest.BaseURI = new Uri("http://localhost"); // Set the ObjectArtifactID or ObjectArtifactGuid that identifies the instance of the RDO. downloadUrlRequest.Target.ObjectArtifactGuid = new Guid("B5FD94BB-0226-4CA5B227-5F3F7BEE4D8A"); // Set the FieldID, FieldName, or FieldGuid of the File File. downloadUrlRequest.Target.FieldId = 1042467; // STEP 3: Call the GetFileFieldDownloadURL method on the proxy. DownloadURLResponse response; try { response = proxy.Repositories.RDO.GetFileFieldDownloadURL (downloadUrlRequest); } catch (Exception ex) { Relativity | Services API Guide - 139 Console.WriteLine("An error occurred calling GetFileFieldDownloadURL: {0} ", ex.Message); return false; } if (response.Success) { Console.WriteLine("File Field Download URL: {0}", response.URL); } else { Console.WriteLine("An error occurred calling GetFileFieldDownloadURL: {0} ", response.Message); return false; } // STEP 4: Download the File using the URL. HttpClient httpClient = new HttpClient(); httpClient.BaseAddress = new Uri("http://localhost/"); HttpResponseMessage httpResponse = httpClient.GetAsync(response.URL).Result; if (httpResponse.IsSuccessStatusCode) { try { // Read the file. Stream resultStream = httpResponse.Content.ReadAsStreamAsync().Result; var contentDisposition = httpResponse.Content.Headers.ContentDisposition; string fileName = contentDisposition.FileName; using (var fileStream = File.Create("C:\\path" + fileName)) { resultStream.CopyTo(fileStream); } Console.WriteLine("Received file: {0}", fileName); } catch (Exception ex) { // An exception occurred when attempting to read and save the file locally. // Handle the exception. Console.WriteLine("Error saving File locally: {0}", ex.Message); return false; } } else { Console.WriteLine("HTTP response not successful: {0}", httpResponse.StatusCode); return false; Relativity | Services API Guide - 140 } return true; } 6.7.5.5 Clearing a File field You can clear a File field through the Services API. You need to obtain the following identifiers to clear the field: n n ArtifactID of the File field associated with the object type ArtifactID of the object instance with a File fields that needs to be cleared Use this variable for the ArtifactID of the newly created File field: fileFieldArtifactId Use this variable for the newly created instance of our Dynamic Object type: ourFileContainerInstanceArtifactId Create a FileRequest instance as follows: var fileRequest = new FileRequest(proxy.APIOptions); fileRequest.Target.FieldId = fileFieldArtifactId; fileRequest.Target.ObjectArtifactId = ourFileContainerInstanceArtifactId; Call the Clear() method on the RSAPIClient: proxy.Clear(fileRequest); 6.7.6 Fields used by Field type, User, and Group Field, User, and Group objects use special fields that aren't used by other system types. 6.7.6.1 Fields used by the Field objects The Field system type uses specialized properties including IgnoreWarnings and the PaneIcon. In addition, the Result object available when a Field is create contains the specialized properties WarningMessage and IsWarning. WarningMessage and IsWarning properties The Create function can return warning messages when a Field object is created. If there are warnings, these messages are set on the WarningMessage property of the Result object, and IsWarning property is set to True. Since the WarningMessage property has a List datatype, multiple warning messages can be returned if necessary. Relativity | Services API Guide - 141 IgnoreWarnings property The Field DTO contains an IgnoreWarnings Boolean property, which you can use to control how warning messages are handled. When the IgnoreWarnings property is set to False and warnings are returned, the Create call will fail. When IgnoreWarnings property is set to True and warnings are returned, the Create call will succeed. In most cases, the code calling the API handles messages returned when the IgnoreWarnings property is set to False, and a Create call returns warnings. Depending on the programming logic, the IgnoreWarnings property could be set to True, and the Create call could be repeated when the warnings should be ignored. When Fields are created, the Message property of a Result object can return multiple errors. Each error will be prefixed with the string API_Field_Validation_Error. This functionality contrasts with other areas in the API, which return only a single error message. View sample code illustrating how to use IgnoreWarnings property on a Field public void Create_Field_With_IgnoreWarnings() { try { using (IRSAPIClient proxy = new RSAPIClient(new Uri("net.pipe://localhost/Relativity.Services"), new IntegratedAuthCredentials())) { proxy.APIOptions.WorkspaceID = WORKSPACE_ID; var fieldDTO = new kCura.Relativity.Client.DTOs.Field(); fieldDTO.Name = "Test Field with Warning 1"; fieldDTO.FieldTypeID = (Int32)FieldType.FixedLengthText; fieldDTO.ObjectType = new kCura.Relativity.Client.DTOs.ObjectType() { DescriptorArtifactTypeID = (Int32)ArtifactType.Document }; fieldDTO.Length = 100; fieldDTO.IsRequired = false; fieldDTO.IncludeInTextIndex = false; fieldDTO.Unicode = false; fieldDTO.AllowHTML = false; fieldDTO.OpenToAssociations = false; fieldDTO.Linked = false; fieldDTO.AllowSortTally = false; fieldDTO.Width = "100"; fieldDTO.Wrapping = false; fieldDTO.AllowPivot = false; fieldDTO.IsRelational = false; // Set AllowGroupBy to true, which should trigger the warning message: // "Setting Allow Group By on a text field containing a large number can // of values can negatively impact system performance." Relativity | Services API Guide - 142 // When Ignore Warnings Create call fails. // When Ignore Warnings Create call succeeds. fieldDTO.AllowGroupBy = fieldDTO.IgnoreWarnings is set to false and warnings are returned, the is set to true and warnings are returned, the true; = false; WriteResultSet<kCura.Relativity.Client.DTOs.Field> writeResults = proxy.Repositories.Field.Create(fieldDTO); if (writeResults.Success) { Console.WriteLine(string.Format("Field with name {0} was created with ArtifacT ID {1}.", fieldDTO.Name, writeResults.Results[0].Artifact.ArtifactID)); } else { Console.WriteLine(string.Format("An error occurred creating field: {0}", writeResults.Message)); for (Int32 i = 0; i <= writeResults.Results.Count - 1; i++) { if (!writeResults.Results[i].Success) { Console.WriteLine(String.Format("An error occurred in create request {0}: {1}", i, writeResults.Results[i].Message)); foreach (string warning in writeResults.Results[0].WarningMessages) { Console.WriteLine("Warning: {0}", warning); } } } } } } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); } Relativity | Services API Guide - 143 } PaneIcon property When you create a Relational Field, you must set a property for the pane icon that is displayed in the Relativity viewer. This icon should be a PNG of 16 x 16 pixels. On DTOs, the pane icon uses the RelationalFieldIcon object, which contains the same Data and Path properties as the FileValue object. You must set the Path and Data properties on the PaneIcon when creating a Relational Field. The Field DTO has a property called PaneIcon that takes a RelationalFieldIcon object as its value. On query and read operations, only the Path value is returned. This code sample illustrates how to create a Relational Field with a PaneIcon. It uses the RelationalFieldIcon. var fieldDTO = new DTOs.Field(); string path = "c:\\path\\icon.png"; System.IO.FileStream ifStream = System.IO.File.OpenRead(path); Int32 len = (Int32)ifStream.Length; System.IO.BinaryReader reader = new System.IO.BinaryReader(ifStream); byte[] byteArr = new byte[len]; reader.Read(byteArr, 0, len); fieldDTO.PaneIcon = new RelationalFieldIcon(path, byteArr); /// Set additional fields on the Field DTO. DTOs.WriteResultSet<DTOs.RDO> results = client.Repositories.Field.Create (fieldDTO); This code sample illustrates how to read a PaneIcon field on a Field. DTOs.Field dto = new DTOs.Field(1038285); dto.Fields.Add(new DTOs.FieldValue(DTOs.FieldFieldNames.PaneIcon)); var readResults = proxy.Repositories.Field.Read(dto); DTOs.RelationalFieldIcon paneIcon = readResults.Results[0].Artifact [DTOs.FieldFieldNames.PaneIcon].ValueAsRelationalFieldIcon; 6.7.6.2 Fields used by Group and User objects Groups and Users require several special fields that aren't used by other system types. These fields include Password, User, and Group. Password fields To create a User, the Password, User Must Change Password on Next Login, and Send New Password To fields are required password settings. Use these general guidelines when working with password fields: Relativity | Services API Guide - 144 n n When working with Users, query for the values needed and save their Choice IDs in variables or a cache as you would for other Single Choice fields. Single Choice fields accept Choice IDs rather than Choice names. The general guidelines for creating a User also apply on update, except that the Password field can be set to the Choice called Use Current Password. In this case, no other password fields are required, except for User Must Change Password On Next Login. This setting indicates that the password shouldn't be altered. Note: These Fields exist as properties on the User DTO. Password Password is a Single Choice field. Use the following guidelines when working with this field: n n n n n Set this Field to one of these Choice values: Auto-generate Password or Manually set password. Retrieve the Choice IDs for this Field by calling the Query method and passing in a Query object with a TextCondition on the field named Choice Type with Password as the text. When the Password field is set to Auto-generate Password, a valid password is automatically generated for the user. When the Password field is set to Manually set password, a Field named New Password is required. This Field is used to provide the new password when a User is being created. Make sure that the password meets the following requirements: o o o o Limited to between 8 and 50 characters in length. Contains at least one uppercase letter, one lower case letter, one numeral, and one special character. Doesn't contain the special characters backslash (\), quotation mark ("), greater than (>), or less than (<). Can't be one of the previous X passwords, where X is the value in the EDDS Configuration Table named MaxPasswordHistory. User Must Change Password on Next Login User Must Change Password on Next Login is a Yes/No field. Send New Password To Send New Password To field is a Single Choice Field. Use the following guidelines when working with this field: n Set the Send New Password To field to one of these Choice ID values: Me (email) - Sends the password to the user logged in to the Services API. This user (email) - Sends the password to the user being created or edited. o Return - Create or Update call returns the password in a field named Password in the MetaDataArtifact property of a Result object. Retrieve the Choice IDs for the Send New Password To field by calling the Query() method and passing in a Query object with a TextCondition on the field named Choice Type with Send new password to as the text. Configuration settings for sending email messages are stored in the EDDS Configuration Table. o o n n Groups field When creating or updating a User, you can include the Groups field to set or modify the groups that include the user as a member. A Groups property also exists on the Group DTO. Follow these guidelines when working Relativity | Services API Guide - 145 with the Groups field: n n n n On create, the value of the Groups field is a list of ArtifactIDs, which indicate the groups that will include the user as a member. On create, don't include the Everyone group in the list of ArtifactIDs. The user is automatically added to the Everyone group. If an error while adding a user to a group, the user will still be created. On update, the value of the Groups field is a MultiUpdateValue object, which contains the Value and Behavior properties. The Value property is a list of ArtifactIDs, and Behavior property specifies how the Artifact IDs are applied to the user. Note: You can only use the MultiUpdateValue class as a parameter when you update User and Group objects. This class has a Behavior property, which is a MultiUpdateBehavior enum. This enumeration is also available only for use with User and Group objects. n n When using DTOs, the Groups property takes a FieldValueList containing a List of Group DTOs. If the behavior is set to Update, you must include the Everyone group in the list of ArtifactIDs. Users field You can include the User field when you want to set or modify users assigned to the Group that you are updating or creating. Follow these guidelines when working with the Users field: n n On create, the value of the Users field is a list of ArtifactIDs that indicate the users that you want included in a group. On update, the value of the Users field is MultiUpdateValue object, which contains the Value and Behavior properties. The Value property is a list of ArtifactIDs, and Behavior property specifies how the ArtifactIDs should be applied to the user. Note: You can only use the MultiUpdateValue class as a parameter when you update User and Group objects. This class has a Behavior property, which is a MultiUpdateBehavior enum. This enumeration is also available only for use with User and Group objects. n When using the DTOs, the Users property takes a MultiUserFieldValueList. When you have a .NET List of User DTOs (FieldValueList<DTOs.Choice>), the UpdateBehavior property indicates how the IDs in the FieldValueList are applied to the field (Replace, Add, or Remove). The default behavior is Replace, when no value is specified. 6.7.7 Constant Field names The Services API supports constant strings for Field names on multiple Relativity objects. Most of these classes extend the base class ArtifactFieldNames. You may also want to consider using enumerations or constants instead of strings in your code. The Services API uses these constant Field names when you enable the StrictMode property or use DTOs. See the following resources: n n n Data Transfer Objects (DTOs) on page 67 StrictMode property and Field directives on page 73 Best practices for the Services API on page 75 ArtifactFieldNames (base class) Relativity | Services API Guide - 146 Property SystemCreatedBy SystemCreatedOn SystemLastModifiedBy SystemLastModifiedOn TextIdentifier Field Name "System Created By" "System Created On" "System Last Modified By" "System Last Modified On" "Relativity Text Identifier" ArtifactQueryFieldNames (doesn't extend ArtifactFieldNames) Property ArtifactID ArtifactGUID ParentArtifactID Field Name "Artifact ID" "Artifact GUID" "Parent Artifact ID" BatchFieldNames Property AssignedTo BatchNumber BatchSet BatchSize BatchStatus BatchUnit Name Reviewed Field Name "Assigned To" "Batch Number" "Batch Set" "Batch Size" "Batch Status" "Batch Unit" "Name" "Reviewed" BatchSetFieldNames Property AutoBatch AutoCreateRateMinutes BatchDataSource BatchPrefix BatchUnitField DocumentsToBeBatched FamilyField LastErrorReported LastSuccessfulRun MaximumBatchSize MinimumBatchSize Name ReviewedField Status ClientFieldNames Relativity | Services API Guide - 147 Field Name "Auto Batch" "Auto Create Rate(minutes)" "Batch Data Source" "Batch Prefix" "Batch Unit Field" "Documents to be Batched" "Family Field" "Last Error Reported" "Last Successful Run" "Maximum Batch Size" "Minimum Batch Size" "Name" "Reviewed Field" "Status" Property ClientNumber Name Status Field Name "Client Number" "Name" "Status" ChoiceFieldNames Property ChoiceTypeID HighlightStyleID Name ObjectTypeName Order RelativityApplications Field Name "Choice Type ID" "Highlight Style ID" "Name" "Object Type Name" "Order" "Relativity Applications" DocumentFieldNames Property Batch Batch_AssignedTo Batch_BatchSet Batch_Status FileIcon FolderName HasImages HasInlineTags HasNative RelativityImageCount RelativityNativeFileLocation RelativityNativeTimeZoneOffset RelativityNativeType SupportedByViewer TimeZoneField Field Name "Batch" "Batch::Assigned To" "Batch::Batch Set" "Batch::Status" "File Icon" "Folder Name" "Has Images" "Has Inline Tags" "Has Native" "Relativity Image Count" "Relativity Native File Location" "Relativity Native Time Zone Offset" "Relativity Native Type" "Supported By Viewer" "Time Zone Field" ErrorFieldNames Property FullError Message SendNotification Server Source URL Workspace FieldFieldNames Relativity | Services API Guide - 148 Field Name "Full Error" "Message" "Send Notification" "Server" "Source" "URL" "Workspace" Property AllowGroupBy AllowHTML AllowPivot AllowSortTally AssociativeObjectType AutoAddChoices AvailableInFieldTree AvailableInViewer Choices FieldTreeView FieldTypeID FilterType Formatting FriendlyName IgnoreWarnings ImportBehavior IncludeInTextIndex IsIdentifier IsRelational IsRequired KeyboardShortcut Length Linked Name NoValue ObjectType OpenToAssociations Order PaneIcon PopupPickerView PropagateTo RelationalView RelativityApplications Unicode Width Wrapping YesValue Field Name "Allow Group By" "Allow HTML" "Allow Pivot" "Allow Sort/Tally" "Associative Object Type" "Auto Add Choices" "Available In Field Tree" "Available In Viewer" "Choices" "Field Tree View" "Field Type ID" "Filter Type" "Formatting" "Friendly Name" "Ignore Warnings" "Import Behavior" "Include in Text Index" "Is Identifier" "Is Relational" "Is Required" "Keyboard Shortcut" "Length" "Linked" "Name" "No Value" "Object Type" "Open to Associations" "Pane Order" "Pane Icon" "Popup Picker View" "Propagate To" "Relational View" "Relativity Applications" "Unicode" "Width" "Wrapping" "Yes Value" FolderFieldNames Property Name Relativity | Services API Guide - 149 Field Name "Name" GroupFieldNames Property GroupType Name Users WorkspaceGroupIDs Workspaces Field Name "Group Type" "Name" "Users" "Workspace Group IDs" "Workspaces" LayoutFieldNames Property Name ObjectType Order OverwriteProtection RelativityApplications Field Name "Name" "Object Type" "Order" "Overwrite Protection" "Relativity Applications" MarkupSetFieldNames Property Name Order RedactionText Field Name "Name" "Order" "Redaction Text" ObjectTypeFieldNames Property CopyInstancesOnWorkspaceCreation DescriptorArtifactTypeID Dynamic Name ParentArtifactTypeID Pivot RelativityApplications SnapshotAuditingEnabledOnDelete ViewEnabled Field Name "Copy Instances On Workspace Creation" "Descriptor Artifact Type ID" "Dynamic" "Name" "Parent ArtifactTypeID" "Pivot" "Relativity Applications" "Snapshot Auditing Enabled On Delete" "View Enabled" RDOFieldNames (doesn't extend ArtifactFieldNames) Property Name Field Name "Name" RelativityApplicationFieldNames Property AgentTypes ApplicationFields ApplicationInstallationStatus Relativity | Services API Guide - 150 Field Name "Agent Types" "Fields" "Application Installation Status" Property ApplicationIsDirty Choices CustomPages EventHandlers LastExported Layouts Locked Name ObjectRules ObjectTypes OriginSignature RelativityScripts TabDisplay Tabs Version Views Field Name "Application Is Dirty" "Choices" "Custom Pages" "Event Handlers" "Last Exported" "Layouts" "Locked" "Name" "Object Rules" "Object Types" "Origin Signature" "Relativity Scripts" "Tab Display" "Tabs" "Version" "Views" RelativityScriptFieldNames Property Body Category Description Key Name RelativityApplications Version Field Name "Body" "Category" "Description" "Key" "Name" "Relativity Applications" "Version" TabFieldNames Property ExternalLink IsDefault IsVisible LinkType Name ObjectType Order RelativityApplications TabDisplay UserFieldNames Relativity | Services API Guide - 151 Field Name "External Link" "Is Default" "Is Visible" "Link Type" "Name" "Object Type" "Order" "Relativity Applications" "Tab Display" Property AdvancedSearchPublicByDefault AuthenticationData BetaUser ChangePassword ChangePasswordNextLogin ChangeSettings Client DataFocus DefaultSelectedFileType DocumentSkip EmailAddress EnforceViewerCompatibility FirstName FullName Groups InvalidLoginAttempts ItemListPageLength KeyboardShortcuts LastName MaximumPasswordAge NativeViewerCacheAhead Password PasswordAction PasswordLastResetOn RelativityAccess SendPasswordTo ShowFilters SkipDefaultPreference TrustedIPs Type Workspaces WorkspaceUserIDs Field Name "Advanced Search Public By Default" "Authentication Data" "Beta User" "Change Password" "Change Password Next Login" "Change Settings" "Client" "Data Focus" "Default Selected File Type" "Document Skip" "Email Address" "Enforce Viewer Compatibility" "First Name" "Full Name" "Groups" "Invalid Login Attempts" "Item List Page Length" "Keyboard Shortcuts" "Last Name" "Maximum Password Age" "Native Viewer Cache Ahead" "Password" "Password Action" "Password Last Reset On" "Relativity Access" "Send Password To" "Show Filters" "Skip Default Preference" "Trusted IPs" "Type" "Workspaces" "Workspace User IDs" ViewFieldNames Property DisplayField GroupDefinitionField IndentationDefinitionField IsVisible Name ObjectType Relativity | Services API Guide - 152 Field Name "Display Field" "Group Definition Field" "Indentation Definition Field" "Is Visible" "Name" "Object Type" Property Order RelativityApplications RenderLinks VisibleInDropdown VisualizationType Field Name "Order" "Relativity Applications" "Render Links" "Visible In Dropdown" "Visualization Type" WorkspaceFieldNames Property Accessible Client DatabaseLocation DefaultFileLocation DownloadHandlerApplicationPath MatterID Name ResourcePoolID RootFolderID ServerID SQLFullTextLanguageCodeID Status Field Name "Accessible" "Client" "Database Location" "Default File Location" "Download Handler Application Path" "Matter ID" "Name" "Resource Pool ID" "Root Folder ID" "Server ID" "SQL Full Text Language Code ID" "Status" 6.8 Folder Within a Relativity workspace, you can use folders to organize content. For more information, see the Relativity 8.1 Documentation site. The Services API supports perform create,read, delete, and query operations on a Folder DTO. 6.8.1 Creating a Folder You can use the Create() method on the Folder repository to add a new folder to Relativity as illustrated in this code sample. public static bool Create_Folder_Using_Repository(IRSAPIClient proxy) { //STEP 1: Create a Folder object. DTOs.Folder folder = new DTOs.Folder { ParentArtifact = new DTOs.Artifact (1003697), Name = "My New Folder" }; //STEP 2: Set Folder properties. //STEP 3: Create the Folder. DTOs.WriteResultSet<DTOs.Folder> results; try Relativity | Services API Guide - 153 { results = proxy.Repositories.Folder.Create(folder); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } //STEP 4: Check for success. if (!results.Success) { Console.WriteLine("Error: " + results.Message); return false; } else { Console.Write("Folder created successfully."); } return true; } 6.8.2 Reading a Folder To read Field values on a Folder, you can use the Read() method on the Folder repository as illustrated in this code sample. public static bool Read_Folder_Using_Repository(IRSAPIClient proxy) { // STEP 1: Read current values. Folder requestArtifact = new Folder(1003697); requestArtifact.Fields.Add(new FieldValue(FolderFieldNames.Name)); ResultSet<Folder> readResult1; try { readResult1 = proxy.Repositories.Folder.Read(requestArtifact); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred while reading the folder: {0}", ex.Message)); return false; } Relativity | Services API Guide - 154 if (!readResult1.Success) { Console.WriteLine("Error reading the folder: " + readResult1.Message); return false; } // STEP 2: Get the folder from the read results. Folder readArtifact = readResult1.Results.FirstOrDefault().Artifact; if (readArtifact == null) { Console.WriteLine("There is no folder with ArtifactID 1038603"); return false; } else { Console.WriteLine(String.Format("ArtifactID:{0} ParentArtifactID:{1} {2}", readArtifact.ArtifactID, readArtifact.ParentArtifact.ArtifactID, readArtifact.Name)); } return true; } 6.8.3 Deleting a Folder You can use the Delete() method on a Folder repository to remove it as illustrated in this code sample. public static bool Delete_Folder_Using_Repository(IRSAPIClient proxy) { // STEP 1: Read current values. DTOs.Folder requestArtifact = new Folder(1036275); ResultSet<DTOs.Folder> readResult1; try { readResult1 = proxy.Repositories.Folder.Read(requestArtifact); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred while reading the folder: {0}", ex.Message)); return false; } Relativity | Services API Guide - 155 if (!readResult1.Success) { Console.WriteLine("Error reading the folder: " + readResult1.Message); return false; } // STEP 2: Get the folder from the read results. DTOs.Folder readArtifact = readResult1.Results.FirstOrDefault().Artifact; if (readArtifact == null) { Console.WriteLine("There is no folder with ArtifactID 1037993"); return false; } // STEP 3: Delete the folder. WriteResultSet<DTOs.Folder> deleteResult; try { deleteResult = proxy.Repositories.Folder.Delete(requestArtifact); } catch (Exception ex) { Console.WriteLine("An error occurred deleting the folder: {0}", ex.Message); return false; } if (!deleteResult.Success) { Console.WriteLine("Error deleting the folder: " + deleteResult.Message); return false; } // STEP 4: To confirm the deletion, try to find the folder again. // Since the folder doesn't exist, the read operation fails. (ResultSet.Success is set to false). // You can query for the folder's ArtifactID. The result is Success with 0 results. WholeNumberCondition artifactIDCondition = new WholeNumberCondition(ArtifactQueryFieldNames.ArtifactID, NumericConditionEnum.EqualTo, 1037993); DTOs.Query<DTOs.Folder> query = new DTOs.Query<DTOs.Folder> { Condition = artifactIDCondition }; Relativity | Services API Guide - 156 QueryResultSet<DTOs.Folder> queryResult; try { queryResult = proxy.Repositories.Folder.Query(query); } catch (Exception ex) { Console.WriteLine("An error occurred querying the folder after it was deleted: {0}", ex.Message); return false; } if (queryResult.Success) { if (queryResult.Results.Count == 0) Console.WriteLine("Delete succeeded, the folder has been deleted"); } else { Console.WriteLine("Error querying the folder after deletion: " + queryResult.Message); return false; } return true; } 6.8.4 Querying for a Folder To query for a Folder, you can use the fields listed in the following table. For more information, see Querying on page 227. Artifact ID Name Parent Artifact ID Parent Artifact Name Fields for Folder queries System Created By System Created On System Last Modified By System Last Modified On The following sample code illustrates how to query for Folder DTOs using a repository. public static bool Query_Folder_Using_Repository(IRSAPIClient proxy) { // STEP 1: Create the query. Query<Folder> query = new Query<kCura.Relativity.Client.DTOs.Folder>(); query.Fields.Add(new FieldValue(FolderFieldNames.Name)); Relativity | Services API Guide - 157 ResultSet<Folder> readResult1; // STEP 2: Query for the folders. try { readResult1 = proxy.Repositories.Folder.Query(query); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred while querying for the folder: {0}", ex.Message)); return false; } if (!readResult1.Success) { Console.WriteLine("Error reading the folder: " + readResult1.Message); return false; } // STEP 3: Use the results. foreach (Result<Folder> folder in readResult1.Results) { Console.WriteLine(String.Format("ArtifactID:{0} ParentArtifactID:{1} {2}", folder.Artifact.ArtifactID, folder.Artifact.ParentArtifact.ArtifactID, folder.Artifact.Name)); } return true; } 6.9 Group Within Relativity, you can use groups to organize users. You can then assign a specific set of permissions to each group. For more information, see Groups in the Relativity 8.1 Documentation site. The Services API supports create, read, delete, and query operations on a Group DTO. 6.9.1 Creating a Group You can add a Group to Relativity by calling the Create() method on the Group repository as illustrated in this code sample. See Fields used by Group and User objects on page 144. public static bool Create_Group(IRSAPIClient proxy) { // STEP 1: Create a DTO and set its properties. Relativity | Services API Guide - 158 DTOs.Group newGroup = new DTOs.Group(); newGroup.Name = "Sample Group"; // STEP 2: Create a WriteResultSet. It provide details after the create operation completes. WriteResultSet<DTOs.Group> resultSet = new WriteResultSet<DTOs.Group>(); // STEP 3: Create the new Group. try { resultSet = proxy.Repositories.Group.Create(newGroup); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } // Check for success. if (!resultSet.Success) { Console.WriteLine("The Create operation failed.{0}{1} ",Environment.NewLine, resultSet.Message); return false; } // Output the results. Console.WriteLine("The Create succeeded."); DTOs.Group createdGroup = resultSet.Results[0].Artifact; Console.WriteLine("{0}The Artifact of the New Group is: {1}", Environment.NewLine, createdGroup.ArtifactID); // Console.WriteLine("The name of the Group is: {0}", createdGroup.GroupName); return true; } 6.9.2 Reading a Group To read Field values, you can use the Read() method on the Group repository as illustrated in this code sample. public static bool Read_Group(IRSAPIClient proxy) { Relativity | Services API Guide - 159 // STEP 1: Create a DTO with criteria you want to read. DTOs.Group groupToRead = new DTOs.Group(1016219); groupToRead.Fields = FieldValue.AllFields; // STEP 2: Create ResultSet to store Results. ResultSet<DTOs.Group> resultSet = new ResultSet<DTOs.Group>(); // STEP 3: Perform the read operation. try { resultSet = proxy.Repositories.Group.Read(groupToRead); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } // Check for success. if (!resultSet.Success) { Console.WriteLine("The Read operation failed.{0}{1}", Environment.NewLine, resultSet.Message); return false; } // Output the results. Console.WriteLine("Read completed successfully."); DTOs.Group readGroup = resultSet.Results[0].Artifact; SampleHelpers.APIHelpers.Print_All_Properties(readGroup); return true; } 6.9.3 Updating a Group You can use the Update() on the Group repository to modify its properties as illustrated in this code sample. See Fields used by Group and User objects on page 144. public static bool Update_Group(IRSAPIClient proxy) { // STEP 1: Create DTO, and set its properties. DTOs.Group newGroup = new DTOs.Group(1016219); newGroup.Name = "Sample Group"; Relativity | Services API Guide - 160 // STEP 2: Create a WriteResultSet. It provides details after the update operation. WriteResultSet<DTOs.Group> resultSet = new WriteResultSet<DTOs.Group>(); // STEP 3: Perform the update operation. try { resultSet = proxy.Repositories.Group.Update(newGroup); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } // Check for success. if (!resultSet.Success) { Console.WriteLine("The Update operation failed.{0}{1} ",Environment.NewLine, resultSet.Message); return false; } Console.WriteLine("Updated completed successfully."); DTOs.Group updatedGroup = resultSet.Results[0].Artifact; // Console.WriteLine("The updated name of the Group is: {0}", updatedGroup.GroupName); return true; } 6.9.4 Deleting a Group You can remove a Group from Relativity by calling the Delete() method on the Group repository as illustrated in this code sample. public static bool Delete_Group(IRSAPIClient proxy) { // STEP 1: Create a DTO populated with criteria for a DTO you want to delete. DTOs.Group groupToDelete = new DTOs.Group(1016219); // STEP 2: Create a WriteResultSet. It provides details after the delete operation completes. WriteResultSet<DTOs.Group> resultSet = new WriteResultSet<DTOs.Group>(); Relativity | Services API Guide - 161 // STEP 3: Perform the delete operation. try { resultSet = proxy.Repositories.Group.Delete(groupToDelete); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } // Check for success. if (!resultSet.Success) { Console.WriteLine("The Delete operation failed.{0}{1} ",Environment.NewLine, resultSet.Message); return false; } // Output the results. Console.WriteLine("Delete completed successfully."); DTOs.Group deletedGroup = resultSet.Results.ElementAt(0).Artifact; Console.WriteLine("The Artifact ID of the deleted Group is: {0}", deletedGroup.ArtifactID); return true; } 6.9.5 Querying for a Group This code sample illustrates how to set query conditions, call the Query() method on the Group repository, and iterate through the result set. public static bool Query_Group(IRSAPIClient proxy) { // STEP 1: Create a Query and ObjectsCondition. It provides details after the query operation. ObjectsCondition workspaceCondition = new ObjectsCondition("Workspaces", ObjectsConditionEnum.AnyOfThese, new Int32[] { 1016204 }); Query<DTOs.Group> query = new DTOs.Query<DTOs.Group> { Condition = workspaceCondition }; query.Fields = FieldValue.AllFields; Relativity | Services API Guide - 162 // STEP 2: Create QueryResultSet to collect information about the DTO after the query completes. QueryResultSet<DTOs.Group> resultSet = new QueryResultSet<DTOs.Group>(); // STEP 3: Perform the query. try { resultSet = proxy.Repositories.Group.Query(query, 0); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } // Check for success. if (!resultSet.Success) { Console.WriteLine("The Query operation failed.{0}{1}", Environment.NewLine, resultSet.Message); return false; } // Output the results. Console.WriteLine(string.Format("Number of Groups returned: {0}", resultSet.Results.Count)); foreach (DTOs.Result<DTOs.Group> groupResult in resultSet.Results) { Console.WriteLine(string.Format("{0}Name:{1}", Environment.NewLine, groupResult.Artifact.Name)); Console.WriteLine(string.Format("ArtifactID:{0}", groupResult.Artifact.ArtifactID)); } return true; } 6.10 Layout In Relativity, layouts are web-based coding forms that enable users to view and edit document fields. For more information, see Layouts in the Relativity 8.1 Documentation site. The Services API supports read and query operations on a Layout DTO. Relativity | Services API Guide - 163 6.10.1 Reading a Layout To read Field values on a Layout, you can use the Read() method on the Layout repository as illustrated in this code sample. public static bool Read_Layout_Using_Repository(IRSAPIClient proxy) { //STEP 1: Create a Layout DTO for the Layout to be read. DTOs.Layout layoutDTO = new DTOs.Layout(1036682); layoutDTO.Fields = FieldValue.AllFields; ResultSet<DTOs.Layout> layoutReadResults = new ResultSet<DTOs.Layout>(); //STEP 2: Try to read the Layout. try { layoutReadResults = proxy.Repositories.Layout.Read(layoutDTO); } catch (Exception ex) { Console.WriteLine("An error occurred reading the layout: {0}", ex.Message); return false; } //STEP 3: Check for success. if (!layoutReadResults.Success) { Console.WriteLine("An error occurred reading the layout: {0}", layoutReadResults.Message); foreach (Result<DTOs.Layout> readResult in layoutReadResults.Results) { if (!readResult.Success) { Console.WriteLine(" An error occurred in read request: {0}", readResult.Message); } } return false; } //STEP 4: Output the name. Console.WriteLine("Successfully read the layout '{0}'!", layoutReadResults.Results[0].Artifact.Name); return true; } Relativity | Services API Guide - 164 6.10.2 Querying for a Layout To query for a Layout, you can use the fields listed in the following table. For more information, see Querying on page 227. ArtifactID Created By Created By Keywords Last Modified By Last Modified On Name Fields for Layout queries Notes Object Type Order in Dropdown Overwrite Protection Owners Relativity Applications Security This code sample illustrates how to set query conditions, call the Query() method on the Layout repository, and iterate through the result set. public static bool Query_Layout_Using_Repository(IRSAPIClient proxy) { //STEP 1: Create the query criteria. ObjectsCondition relAppCondition = new ObjectsCondition("Relativity Applications", ObjectsConditionEnum.AllOfThese, new int[] { 1035699 }); Query<DTOs.Layout> layoutQuery = new Query<DTOs.Layout> { Condition = relAppCondition, Fields = FieldValue.AllFields }; //STEP 2: Try to query for the Layout. QueryResultSet<DTOs.Layout> layoutQueryResults = null; try { layoutQueryResults = proxy.Repositories.Layout.Query(layoutQuery); } catch (Exception ex) { Console.WriteLine("An error occurred querying for layouts: {0}", ex.Message); return false; } //STEP 3: Check for success. if (!layoutQueryResults.Success) Relativity | Services API Guide - 165 { Console.WriteLine("An error occurred querying for layouts: {0}", layoutQueryResults.Message); return false; } //STEP 4: Display the results. Console.WriteLine("Number of layouts returned: {0}", layoutQueryResults.Results.Count); foreach (Result<DTOs.Layout> layoutResult in layoutQueryResults.Results) { DTOs.Layout curLayout = layoutResult.Artifact; Console.WriteLine("Layout Name: {0}", curLayout.TextIdentifier); Console.WriteLine("Layout ArtifactID: {0}", curLayout.ArtifactID); } return true; } 6.11 MarkupSet In Relativity, markup sets are securable highlights and redactions available to reviewers. For more information, see Markup sets in the Relativity 8.1 Documentation site. The Services API supports all CRUD and query operations on a MarkupSet DTO. 6.11.1 Creating a MarkupSet You can add a MarkupSet to Relativity by calling the Create() method on the MarkupSet repository as illustrated in this code sample. public static bool Create_MarkupSet_Using_Repository(IRSAPIClient proxy) { // STEP 1: Create a MarkupSet DTO and populate the fields. DTOs.MarkupSet markupSetDTO = new DTOs.MarkupSet(); markupSetDTO.Name = "New Markup Set"; markupSetDTO.Order = 10; markupSetDTO.RedactionText = new List<String>(new string[] {"Each", "string", "here", "is", "on", "a", "new", "line."}); // STEP 2: Call the Create() method on the repository and pass in the DTO. DTOs.WriteResultSet<DTOs.MarkupSet> createResults; try { createResults = proxy.Repositories.MarkupSet.Create(markupSetDTO); Relativity | Services API Guide - 166 } catch (Exception ex) { Console.WriteLine("An error occurred creating the Markup Set: {0}", ex.Message); return false; } // STEP 3: Check for success. if (!createResults.Success) { Console.WriteLine("An error occurred creating the Markup Set: {0}", createResults.Results[0].Message); return false; } else { Console.WriteLine("Successfully created Markup Set '{0}'!", markupSetDTO.Name); } return true; } 6.11.2 Reading a MarkupSet To read Field values, you can use the Read() method on the MarkupSet repository as illustrated in this code sample. public static bool Read_MarkupSet_Using_Repository(IRSAPIClient proxy) { // STEP 1: Create the MarkupSet DTO. kCura.Relativity.Client.DTOs.MarkupSet markupSet = new kCura.Relativity.Client.DTOs.MarkupSet(1039144); markupSet.Fields = kCura.Relativity.Client.DTOs.FieldValue.AllFields; // STEP 2: Read the MarkupSet DTO from the repository. kCura.Relativity.Client.DTOs.ResultSet<kCura.Relativity.Client.DTOs.MarkupS et> resultSet; try { resultSet = proxy.Repositories.MarkupSet.Read(markupSet); } catch (Exception ex) Relativity | Services API Guide - 167 { Console.WriteLine("An error occurred reading the MarkupSet: {0}", ex.Message); return false; } // STEP 3: Check for success. if (!resultSet.Success) { Console.WriteLine("An error occurred reading the MarkupSet: {0}", resultSet.Message); return false; } kCura.Relativity.Client.DTOs.MarkupSet markupSetResult = resultSet.Results.FirstOrDefault().Artifact; Console.WriteLine("MarkupSet Name: {0}", markupSetResult.Name); return true; } 6.11.3 Updating a MarkupSet You can use the Update() on the MarkupSet repository to modify its properties as illustrated in this code sample. public static bool Update_MarkupSet_Using_Repository(IRSAPIClient proxy) { // STEP 1: Create the MarkupSet DTO. kCura.Relativity.Client.DTOs.MarkupSet markupSet = new kCura.Relativity.Client.DTOs.MarkupSet(1039144); markupSet.Fields = kCura.Relativity.Client.DTOs.FieldValue.AllFields; // STEP 2: Read the MarkupSet DTO from the repository. kCura.Relativity.Client.DTOs.ResultSet<kCura.Relativity.Client.DTOs.MarkupS et> resultSet; try { resultSet = proxy.Repositories.MarkupSet.Read(markupSet); } catch (Exception ex) { Console.WriteLine("An error occurred reading the MarkupSet: {0}", ex.Message); return false; Relativity | Services API Guide - 168 } // STEP 3: Check for success. if (!resultSet.Success) { Console.WriteLine("An error occurred reading the MarkupSet: {0}", resultSet.Message); return false; } kCura.Relativity.Client.DTOs.MarkupSet markupSetResult = resultSet.Results.FirstOrDefault().Artifact; Console.WriteLine("MarkupSet Name: {0}", markupSetResult.Name); // STEP 4: Update the DTO in the repository. markupSetResult.Name = "Updated Markup Set"; markupSetResult.Order = 20; markupSetResult.RedactionText.Add("New Markup Text"); kCura.Relativity.Client.DTOs.ResultSet<kCura.Relativity.Client.DTOs.MarkupS et> updatedResultSet; try { updatedResultSet = proxy.Repositories.MarkupSet.Update(markupSetResult); } catch (Exception ex) { Console.WriteLine("An error occurred Updating the MarkupSet: {0}", ex.Message); return false; } // STEP 5: Check for success. if (!updatedResultSet.Success) { Console.WriteLine("An error occurred Updating the MarkupSet: {0}", updatedResultSet.Message); return false; } return true; } 6.11.4 Deleting a MarkupSet You can remove a MarkupSet from Relativity by calling the Delete() method on the MarkupSet repository as illustrated in this code sample. Relativity | Services API Guide - 169 public static bool Delete_MarkupSet_Using_Repository(IRSAPIClient proxy) { // STEP 1: Create the MarkupSet DTO. kCura.Relativity.Client.DTOs.MarkupSet markupSet = new kCura.Relativity.Client.DTOs.MarkupSet(1039144); // STEP 2: Delete the MarkupSet from the repository. kCura.Relativity.Client.DTOs.ResultSet<kCura.Relativity.Client.DTOs.MarkupS et> resultSet; try { resultSet = proxy.Repositories.MarkupSet.Delete(markupSet); } catch (Exception ex) { Console.WriteLine("An error occurred deleting the MarkupSet: {0}", ex.Message); return false; } // STEP 3: Check for success. if (!resultSet.Success) { Console.WriteLine("An error occurred deleting the MarkupSet: {0}", resultSet.Message); return false; } return true; } 6.11.5 Querying for a MarkupSet To query for a MarkupSet, you can use the fields listed in the following table. For more information, see Querying on page 227. Artifact ID Created By Created On Name Keywords Last Modified By Fields for MarkupSet queries Last Modified On Notes Order Redaction Text – returned as List<string> to accommodate multiple values. Security This code sample illustrates how to set query conditions, call the Query() method on the MarkupSet repository, and iterate through the result set. Relativity | Services API Guide - 170 public static bool Query_MarkupSet_Using_Repository(IRSAPIClient proxy) { // STEP 1: Create the query criteria. DTOs.Query<DTOs.MarkupSet> markupSetQuery = new DTOs.Query<DTOs.MarkupSet> (); List<String> desiredRedactionText = new List<string>(new string[] { "Each", "string", "here", "is", "on", "a", "new", "line." }); markupSetQuery.Condition = new MultiLineStringCondition (DTOs.MarkupSetFieldNames.RedactionText, TextConditionEnum.EqualTo, desiredRedactionText); markupSetQuery.Fields = DTOs.FieldValue.AllFields; // STEP 2: Call the Query() method on the repository. DTOs.QueryResultSet<DTOs.MarkupSet> queryResults; try { queryResults = proxy.Repositories.MarkupSet.Query(markupSetQuery); } catch (Exception ex) { Console.WriteLine("An error occurred querying for the MarkupSet: {0}", ex.Message); return false; } // STEP 3: Check for success. if (!queryResults.Success) { Console.WriteLine("An error occurred querying for the MarkupSet: {0}", queryResults.Message); return false; } else { Console.WriteLine("Query returned {0} MarkupSets!", queryResults.Results.Count); foreach (DTOs.Result<DTOs.MarkupSet> markupSetResult in queryResults.Results) { Console.WriteLine("MarkupSet Name: {0}", markupSetResult.Artifact.Name); Relativity | Services API Guide - 171 } } return true; } 6.12 ObjectType An ObjectType is Relativity Dynamic Object (RDO) type that you add to a workspace. It is synonymous with ArtifactType. For more information, see Object type details in the Relativity 8.1 Documentation site. The Services API supports all CRUD and query operations on a ObjectType DTO. 6.12.1 Creating an ObjectType You can add an ObjectType to Relativity by calling the Create() method on the ObjectType repository as illustrated in this code sample. public static bool Create_ObjectType_Using_Repository(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = 1016204; // STEP 1: Create an ObjectType DTO and populate the Fields. DTOs.ObjectType objectTypeDTO = new DTOs.ObjectType(); objectTypeDTO.Name = "New Object Type"; objectTypeDTO.ParentArtifactTypeID = 8; objectTypeDTO.SnapshotAuditingEnabledOnDelete = true; objectTypeDTO.Pivot = true; objectTypeDTO.CopyInstancesOnWorkspaceCreation = false; objectTypeDTO.Sampling = true; objectTypeDTO.PersistentLists = false; objectTypeDTO.RelativityApplications = new List<DTOs.RelativityApplication> { new DTOs.RelativityApplication (1037990), new DTOs.RelativityApplication(1037639) }; // STEP 2: Call the Create method on the repository, passing the DTO. WriteResultSet<DTOs.ObjectType> resultSet; try { resultSet = proxy.Repositories.ObjectType.Create(objectTypeDTO); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); return false; } Relativity | Services API Guide - 172 if (!resultSet.Success) { Console.WriteLine("Error: " + resultSet.Message); foreach (Result<DTOs.ObjectType> result in resultSet.Results) { Console.WriteLine("Result Error: " + result.Message); } return false; } Console.WriteLine("Overall status of creating an object type: " + resultSet.Success); Console.WriteLine("New Artifact ID: " + resultSet.Results.FirstOrDefault ().Artifact.ArtifactID); return true; } 6.12.2 Reading an ObjectType To read Field values, you can use the Read() method on the ObjectType repository as illustrated in this code sample. public static bool Read_ObjectType_Using_Repository(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = 1016204; // STEP 1: Call the Read() method on the ObjectType repository and pass an ObjectType DTO. ResultSet<DTOs.ObjectType> results; try { results = proxy.Repositories.ObjectType.Read(new DTOs.ObjectType(1036210) { Fields = FieldValue.AllFields }); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); return false; } if (!results.Success) { Console.WriteLine("Error: " + results.Message); foreach (Result<DTOs.ObjectType> result in results.Results) { Console.WriteLine("Result Error: " + result.Message); Relativity | Services API Guide - 173 } return false; } // STEP 2: Get the Object Type artifact from the read results. DTOs.ObjectType objectTypeArtifact = results.Results.FirstOrDefault ().Artifact; Console.WriteLine("Object Type Artifact ID: " + objectTypeArtifact.ArtifactID); Console.WriteLine("Object Type Name: " + objectTypeArtifact.Name); Console.WriteLine("Object Type Artifact Type ID: " + objectTypeArtifact.DescriptorArtifactTypeID); return true; } 6.12.3 Updating an ObjectType You can use the Update() method on the ObjectType repository to modify its properties as illustrated in this code sample. public static bool Update_ObjectType_Using_Repository(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = 1016204; // STEP 1: Create an ObjectType DTO and populate the Fields you want to update. DTOs.ObjectType objectTypeDTO = new DTOs.ObjectType(1035966); objectTypeDTO.Name = "Renamed Object Type"; // STEP 2: Call the Update() method on the repository and pass the DTO. WriteResultSet<DTOs.ObjectType> resultSet; try { resultSet = proxy.Repositories.ObjectType.Update(objectTypeDTO); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); return false; } if (!resultSet.Success) { Console.WriteLine("Error: " + resultSet.Message); foreach (Result<DTOs.ObjectType> result in resultSet.Results) { Relativity | Services API Guide - 174 Console.WriteLine("Result Error: " + result.Message); } return false; } Console.WriteLine("Overall status of Updating an object type: " + resultSet.Success); // STEP 3: Read back the Artifact for verification. ResultSet<DTOs.ObjectType> readResult; try { readResult = proxy.Repositories.ObjectType.Read(objectTypeDTO); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); return false; } if (!readResult.Success) { Console.WriteLine("Error: " + readResult.Message); foreach (Result<DTOs.ObjectType> result in resultSet.Results) { Console.WriteLine("Result Error: " + result.Message); } return false; } Console.WriteLine("Updated Object Type Name: " + readResult.Results.FirstOrDefault().Artifact.Name); return true; } 6.12.4 Deleting an ObjectType You can remove an ObjectType from Relativity by calling the Delete() method on the ObjectType repository as illustrated in this code sample. public static bool Delete_ObjectType_Using_Repository(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = 1016204; // STEP 1: Call the delete method on the ArtifactType Repository, passing the ObjectType DTO. ResultSet<DTOs.ObjectType> results; try { Relativity | Services API Guide - 175 results = proxy.Repositories.ObjectType.Delete(new DTOs.ObjectType (1038017)); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); return false; } if (results.Success) Console.WriteLine("Status of Delete: " + results.Success); else Console.WriteLine("Error deleting object type: " + results.Results [0].Message); return true; } 6.12.5 Querying for an ObjectType This code sample illustrates how to set query conditions, call the Query() method on the ObjectType repository, and iterate through the result set. public static bool Query_ObjectType_Using_Repository(IRSAPIClient proxy) { // STEP 1: Setup your query criteria. TextCondition criteria = new TextCondition(kCura.Relativity.Client.DTOs.ObjectTypeFieldNames.Name, TextConditionEnum.EqualTo, "Employees"); Query<DTOs.ObjectType> query = new Query<DTOs.ObjectType> { Condition = criteria, Fields = FieldValue.AllFields }; // STEP 2: Call the Query() method in the ObjectType repository. QueryResultSet<DTOs.ObjectType> result = new QueryResultSet<DTOs.ObjectType> (); try { result = proxy.Repositories.ObjectType.Query(query, 0); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; Relativity | Services API Guide - 176 } Console.WriteLine(string.Format("Number of documents returned: {0}", result.TotalCount)); Console.WriteLine(string.Format("Additional Pages of Query Results Available?: {0}", !string.IsNullOrEmpty(result.QueryToken))); // STEP 3: Iterate over the returned ObjectType result. foreach (DTOs.Result<DTOs.ObjectType> objTypeResult in result.Results) { DTOs.ObjectType objType = objTypeResult.Artifact; Console.WriteLine("Object Type Name: " + objTypeResult.Artifact.Name); Console.WriteLine("Object Type Artifact Type ID: " + objTypeResult.Artifact.DescriptorArtifactTypeID); } return true; } 6.13 RDO A Relativity Dynamic Object (RDO) is custom object that you create in a workspace. It has a unique ArtifactTypeID in the workspace where it is created. For more information, see Relativity objects in the Relativity 8.1 Documentation site. The Services API supports all CRUD and query operations on an RDO. 6.13.1 Creating an RDO You can add an RDO to Relativity by calling the Create() method on the RDO repository. This code sample use GUIDs to reference Fields, but you can also reference Fields by their names or ArtifactIDs. See GUIDs in application development on page 78. public void Create_an_RDO_UsingGUIDs() { try { using (IRSAPIClient proxy = new RSAPIClient(new Uri("net.pipe://localhost/Relativity.Services"), new IntegratedAuthCredentials())) { proxy.APIOptions.WorkspaceID = WORKSPACE_ID; // STEP 1: Create an RDO DTO and set the ArtifactTypeGuids. var dto = new RDO(); Relativity | Services API Guide - 177 dto.ArtifactTypeGuids.Add(new Guid("EE5BD2B1-47A8-45CE-AA5B2115B6DD86A4")); // STEP 2: Add FieldValues to the Fields collection. Specify the GUID // of the Field that you want to set. Set the FixedLengthText fields. dto.Fields.Add(new FieldValue(new Guid("37159592-B5B6-4405-AF7410B5728890B4"), "Smith")); dto.Fields.Add(new FieldValue(new Guid("3BDC0971-A87C-414E-9A37FC477279BBAD"), "Joe")); // Set a SingleChoice Type field to a Choice using the GUID that represents // the Choice artifact. dto.Fields.Add(new FieldValue(new Guid("4F06AC67-822A-414F-B6C15D4007E998EF"), new kCura.Relativity.Client.DTOs.Choice(new Guid ("4501A308-5E68-4314-AEDC-4DEB527F12A8")))); // Set A MultiChoice field by creating a MultiChoiceFieldValueList as its value. // The MultiChoiceFieldValueList contains Choice DTOs, which are set using GUIDs that represent // the Choice artifact. var multiChoices =new MultiChoiceFieldValueList( new kCura.Relativity.Client.DTOs.Choice(new Guid("6A1D5E35-B4B3-49629EEB-6E6D3016D40C")), new kCura.Relativity.Client.DTOs.Choice(new Guid("E647DF05-2E51-41E7B6A3-C3F1CD723C54"))); // Set the UpdateBehavior depending on whether you want to Merge or Replace the new values. multiChoices.UpdateBehavior = MultiChoiceUpdateBehavior.Replace; // Set the MultiChoice field using its GUID and set the value to the MultiChoiceFieldValueList. dto.Fields.Add(new FieldValue(new Guid("C9D1C1E7-61B1-46EE-A35D406FC9503DE5"), multiChoices)); // Set a User field to a User DTO. dto.Fields.Add(new FieldValue(new Guid("C1A2DB49-3282-40CF-84A08DEEB4E76764"), new kCura.Relativity.Client.DTOs.User(1015411))); // Set a SingleObject to a DTO for the object. In this example, a SingleObject Field takes a Document. var obj = new kCura.Relativity.Client.DTOs.Document(1038372); Relativity | Services API Guide - 178 dto.Fields.Add(new FieldValue(new Guid("68094937-3E3A-42D4-B71B132C8A4C51F7"), obj)); // Set a MultiObject to a FieldValueList of the Type of objects. In this example, a MultiObject Field takes instances of RDOs. FieldValueList<RDO> objects = new FieldValueList<RDO>(); objects.Add(new RDO(1067600)); objects.Add(new RDO(1067613)); dto.Fields.Add(new FieldValue(new Guid("467D2CDE-7892-4463-9890805B937E3945"), objects)); // STEP 3: Call the Create() method on the RDO Respository. WriteResultSet<RDO> writeResults = proxy.Repositories.RDO.Create(dto); if (writeResults.Success) { Console.WriteLine(string.Format("Object was created with Artifact ID {0}.", writeResults.Results[0].Artifact.ArtifactID)); } else { Console.WriteLine(string.Format("An error occurred creating object: {0}", writeResults.Message)); for (Int32 i = 0; i <= writeResults.Results.Count - 1; i++) { if (!writeResults.Results[i].Success) { Console.WriteLine(String.Format("An error occurred in create request {0}: {1}", i, writeResults.Results [i].Message)); } } } } } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); } } 6.13.2 Creating an RDO as a child object You can create an RDO that is a child of another object other than Workspace. You must set the ParentArtifact property of the child object to the DTO Artifact of its parent object as illustrated in this code sample. Relativity | Services API Guide - 179 // Create an instance of the parent object with ArtifactTypeName “ParentObj”. var rdo = new RDO("ParentObj"); rdo.TextIdentifier = "Test Parent Object"; var parentCreate = proxy.Repositories.RDO.Create(rdo); // Create an instance of the child object with ArtifactTypeName “ChildObj” // and set the ParentArtifact to the parent object instance previously created. var childRDO = new RDO("ChildObj"); childRDO.TextIdentifier = "Child of Test Parent Object"; childRDO.ParentArtifact = new kCura.Relativity.Client.DTOs.Artifact(parentCreate.Results [0].Artifact.ArtifactID); var childCreate = proxy.Repositories.RDO.Create(childRDO); 6.13.3 Reading an RDO To read Field values, you can use the Read() method on the RDO repository as illustrated in this code sample. public void Read_an_RDO_Using_Repository2() { try { using (IRSAPIClient proxy = new RSAPIClient(new Uri("net.pipe://localhost/Relativity.Services"), new IntegratedAuthCredentials())) { proxy.APIOptions.WorkspaceID = WORKSPACE_ID; // STEP 1: Call the Read() method on the RDO repository. RDO employee = new RDO(1067694); employee.ArtifactTypeID = 1000030; employee.Fields = FieldValue.AllFields; ResultSet<RDO> results = new ResultSet<RDO>(); results = proxy.Repositories.RDO.Read(employee); //Check for success. if (!results.Success) { Console.WriteLine("An error occurred!"); Console.WriteLine(results.Message); } else { Console.WriteLine(string.Format("An error occurred reading object: {0}", results.Message)); Relativity | Services API Guide - 180 for (Int32 i = 0; i <= results.Results.Count - 1; i++) { if (!results.Results[i].Success) { Console.WriteLine(String.Format("An error occurred in read request {0}: {1}", i, results.Results[i].Message)); } } } // STEP 2: employee = FieldValue FieldValue FieldValue FieldValue FieldValue FieldValue Get the Artifact from the read results. results.Results.Single().Artifact; lastNameField = employee["Last Name"]; employmentLevelField = employee["Employment Level"]; hrRepField = employee["HR Rep"]; skillsInventoryField = employee["Skills Inventory"]; keyDocumentField = employee["Key Document"]; favoriteDocumentsField = employee["Favorite Documents"]; Console.WriteLine("Artifact: {0} Field: {1} Value: {2}", employee.ArtifactID, lastNameField.Name, lastNameField.ValueAsFixedLengthText); // The SingleChoice field returns a Choice DTO. Console.WriteLine("Artifact: {0} Field: {1} Value: {2}", employee.ArtifactID, employmentLevelField.Name, employmentLevelField.ValueAsSingleChoice.ArtifactID); // The MultipleChoice field returns a MultiChoiceFieldValueList containing a List of Choice DTOs. MultiChoiceFieldValueList multiChoiceList = skillsInventoryField.ValueAsMultipleChoice; for (int index = 0; index < multiChoiceList.Count; index++) { kCura.Relativity.Client.DTOs.Choice choice = multiChoiceList [index]; Console.WriteLine("Artifact: {0} Field: {1} Value: {2}", employee.ArtifactID, skillsInventoryField.Name + " " + "Choice " + index, choice.ArtifactID); } // The User field returns a User DTO. Relativity | Services API Guide - 181 Console.WriteLine("Artifact: {0} Field: {1} Value: {2}", employee.ArtifactID, hrRepField.Name, hrRepField.ValueAsUser.ArtifactID + " - " + hrRepField.ValueAsUser.FullName); // The SingleObject field is returned as an Artifact DTO. kCura.Relativity.Client.DTOs.Artifact documentArtifact = keyDocumentField.ValueAsSingleObject; Console.WriteLine("Artifact: {0} Field: {1} Value: {2}", employee.ArtifactID, keyDocumentField.Name, documentArtifact.ArtifactID); // The MultipleObject field is returned as FieldValueList<Artifact>. FieldValueList<kCura.Relativity.Client.DTOs.Artifact> multiObject = favoriteDocumentsField.GetValueAsMultipleObject<kCura.Relativity.Cl ient.DTOs.Artifact>(); for (int index = 0; index < multiObject.Count; index++) { Int32 id = multiObject[index].ArtifactID; Console.WriteLine("Artifact: {0} Field: {1} Value: {2}", employee.ArtifactID, favoriteDocumentsField.Name + " Object " + index, id); } } } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); } } 6.13.4 Updating an RDO You can use the Update() method on an RDO repository to modify its properties as illustrated in this code sample. public void Update_RDO_Using_Repository() { try { using (IRSAPIClient proxy = new RSAPIClient(new Uri("net.pipe://localhost/Relativity.Services"), new IntegratedAuthCredentials())) { Relativity | Services API Guide - 182 proxy.APIOptions.WorkspaceID = WORKSPACE_ID; // STEP 1: Create an RDO DTO and add the FieldValue that you want to update. RDO artifact = new RDO("Employees", 1067694); artifact.Fields.Add(new FieldValue() {Name = "Last Name", Value = "Smith, Jr."}); // STEP 2: Call the Update() method on the RDO respository. WriteResultSet<RDO> writeResultSet = proxy.Repositories.RDO.Update (artifact); if (!writeResultSet.Success) { Console.WriteLine("Error: " + writeResultSet.Message); for (Int32 i = 0; i <= writeResultSet.Results.Count - 1; i++) { if (!writeResultSet.Results[i].Success) { Console.WriteLine(String.Format("An error occurred in update request {0}: {1}", i, writeResultSet.Results [i].Message)); } } } } } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); } } 6.13.5 Deleting an RDO You can remove an RDO from Relativity by calling the Delete() method on the RDO repository as illustrated in this code sample. public void Delete_RDO() { using (IRSAPIClient proxy = new RSAPIClient(new Uri("net.pipe://localhost/Relativity.Services"), new IntegratedAuthCredentials())) { Relativity | Services API Guide - 183 try { proxy.APIOptions.WorkspaceID = WORKSPACE_ID; //Step 1: Create a DTO for an instance of the "Employees" RDO. var dtoToDelete = new RDO("Employees", 1067605); //Step 2: Call Delete on the RDO Repository and pass the RDO DTO. WriteResultSet<RDO> deleteResult = proxy.Repositories.RDO.Delete (dtoToDelete); if (!deleteResult.Success) { Console.WriteLine("Error deleting the object: " + deleteResult.Message); for (Int32 i = 0; i <= deleteResult.Results.Count - 1; i++) { if (!deleteResult.Results[i].Success) { Console.WriteLine(String.Format("An error occurred in delete request {0}: {1}", i, deleteResult.Results[i].Message)); } } } } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); } } } 6.13.6 Querying for an RDO This code sample illustrates how to set query conditions, call the Query() method on the RDO repository, and iterate through the result set. public void Query_an_RDO_Using_Repository() { using (IRSAPIClient proxy = new RSAPIClient(new Uri("net.pipe://localhost/Relativity.Services"), new IntegratedAuthCredentials())) Relativity | Services API Guide - 184 { try { proxy.APIOptions.WorkspaceID = WORKSPACE_ID; // STEP 1: Setup your query criteria. TextCondition lastNameCriteria = new TextCondition("Last Name", TextConditionEnum.EqualTo, "Smith"); TextCondition firstNameCriteria = new TextCondition("Name", TextConditionEnum.EqualTo, "Mike"); CompositeCondition condition = new CompositeCondition (lastNameCriteria, CompositeConditionEnum.And, firstNameCriteria); kCura.Relativity.Client.DTOs.Query<RDO> query = new Query<RDO> {ArtifactTypeID = 1000030, Condition = condition}; query.Fields.Add(new query.Fields.Add(new query.Fields.Add(new query.Fields.Add(new query.Fields.Add(new FieldValue("Employment Level")); FieldValue("Skills Inventory")); FieldValue("HR Rep")); FieldValue("Last Name")); FieldValue("Name")); // STEP 2: Call the Query() method on the RDO repository. QueryResultSet<RDO> results = new QueryResultSet<RDO>(); results = proxy.Repositories.RDO.Query(query); //Check for success. if (!results.Success) { Console.WriteLine("An error occurred!"); Console.WriteLine(results.Message); } // STEP 3: Get the Artifact from the query results. RDO employee = results.Results.Single().Artifact; Console.WriteLine(">>>" + employee + "<<<"); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); } } } Relativity | Services API Guide - 185 6.14 RelativityApplication You can develop applications that use Relativity Dynamic Objects (RDOs), custom pages, event handlers, and other platform components. For more information, see Application Deployment System on the Relativity 8.1 Developers site. The Services API supports all read and query operations on a RelativityApplication DTO. 6.14.1 Reading a RelativityApplication To read Field values on RelativityApplication object, you can use the Read() method on the RelativityApplication repository as illustrated in this code sample. public static bool ReadRelativityApplicationUsingRepository(IRSAPIClient proxy) { // STEP 1: Create a RelativityApplication DTO for the application that you want to read. RelativityApplication relativityApplication = new RelativityApplication (1037631); relativityApplication.Fields.Add(new FieldValue (RelativityApplicationFieldNames.Name)); relativityApplication.Fields.Add(new FieldValue (RelativityApplicationFieldNames.Version)); ResultSet<RelativityApplication> readResultSet = null; // STEP 2: Try to read the RelativityApplication object. try { readResultSet = proxy.Repositories.RelativityApplication.Read (relativityApplication); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } // STEP 3: Check for success. if (!readResultSet.Success) { Console.WriteLine("An error occurred reading the Relativity Application: {0}", readResultSet.Message); foreach (Result<RelativityApplication> readResult in readResultSet.Results) { Relativity | Services API Guide - 186 if (!readResult.Success) { Console.WriteLine(" An error occurred in the read result: {0}", readResult.Message); } } return false; } RelativityApplication readApplication = readResultSet.Results[0].Artifact; // STEP 4: Display the name and version of the Relativity application. Console.WriteLine("Relativity Application Name: {0}{1}Relativity Application Version: {2}", readApplication.Name, Environment.NewLine, readApplication.Version); return true; } 6.14.2 Deleting a Relativity Application You can call the Delete() method on the RelativityApplication repository to delete an application. The Application Deployment System (ADS) also provides functionality for deleting and uninstalling applications through the Relativity UI. For more information, see e Application Deployment System in the Relativity 8.1 Developers site. public static bool DeleteRelativityApplicationUsingRepository(IRSAPIClient proxy) { // STEP 1: Create a RelativityApplication DTO for the application that you want to delete. RelativityApplication relativityApplication = new RelativityApplication (1037639); WriteResultSet<RelativityApplication> writeResultSet = null; // STEP 2: Try to delete the Relativity Application. try { writeResultSet = proxy.Repositories.RelativityApplication.Delete (relativityApplication); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } Relativity | Services API Guide - 187 //STEP 3: Check for success. if (!writeResultSet.Success) { Console.WriteLine("An error occurred deleting the Relativity Application: {0}", writeResultSet.Message); foreach (Result<RelativityApplication> writeResult in writeResultSet.Results) { if (!writeResult.Success) { Console.WriteLine(" An error occurred in the delete result: {0}", writeResult.Message); } } return false; } return true; } 6.14.3 Querying for a RelativityApplication To query for a RelativityApplication object, you can use the fields listed in the following table. For more information, see Querying on page 227. Guid ID InDevelopment Fields for Application queries Name Version This code sample illustrates how to set query conditions, call the Query() method on the RelativityApplication repository, and iterate through the result set. public static bool QueryRelativityApplicationUsingRepository(IRSAPIClient proxy) { // STEP 1: Create a Relativity Application DTO for the application to query. Query<kCura.Relativity.Client.DTOs.RelativityApplication> query = new Query<RelativityApplication>(); query.Fields.Add(new FieldValue(ArtifactQueryFieldNames.ArtifactID)); query.Fields.Add(new FieldValue(RelativityApplicationFieldNames.Name)); query.Fields.Add(new FieldValue(RelativityApplicationFieldNames.Version)); query.Condition = Relativity | Services API Guide - 188 new WholeNumberCondition(ArtifactQueryFieldNames.ArtifactID, NumericConditionEnum.EqualTo, 1037631); ResultSet<RelativityApplication> readResultSet = null; //STEP 2: Attempt to query the Relativity Application. try { readResultSet = proxy.Repositories.RelativityApplication.Query(query); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } //STEP 3: Check for success. if (!readResultSet.Success) { Console.WriteLine("An error occurred reading the Relativity Application: {0}", readResultSet.Message); foreach (Result<RelativityApplication> readResult in readResultSet.Results) { if (!readResult.Success) { Console.WriteLine(" An error occurred in the read result: {0}", readResult.Message); } } return false; } RelativityApplication readApplication = readResultSet.Results[0].Artifact; //STEP 4: Display the name and version of the Relativity application. Console.WriteLine("Relativity Application Name: {0}{1}Relativity Application Version: {2}", readApplication.Name, Environment.NewLine, readApplication.Version); return true; } 6.15 RelativityScript Using the Services API, you can execute SQL-based scripts that have been added to workspaces through the Relativity web UI. These scripts extend the functionality of the Services API by working directly with the Relativity | Services API Guide - 189 database to retrieve or modify data for report generation or other purposes. For more information, see Script development on the Relativity 8.1 Developers site. The Services API supports read and query operations on the RelativityScript DTO, as well as the functionality for executing a script and retrieving its inputs. 6.15.1 Reading a RelativityScript To read Field values, you can use the Read() method on the RelativityScript repository as illustrated in this code sample. public static bool Read_RelativityScript_Using_Repository(IRSAPIClient proxy) { // STEP 1: Create a RelativityScript DTO for the RelativityScript that you want to read. DTOs.RelativityScript relScriptDTO = new DTOs.RelativityScript(1015032); relScriptDTO.Fields = DTOs.FieldValue.AllFields; // STEP 2: Attempt to read the RelativityScript. DTOs.ResultSet<DTOs.RelativityScript> relScriptReadResults = new DTOs.ResultSet<DTOs.RelativityScript>(); try { relScriptReadResults = proxy.Repositories.RelativityScript.Read (relScriptDTO); } catch (Exception ex) { Console.WriteLine("An error occurred reading the Relativity Script: {0}", ex.Message); return false; } // STEP 3: Check for success. if (!relScriptReadResults.Success) { Console.WriteLine("An error occurred reading the Relativity Script: {0}", relScriptReadResults.Message); foreach (DTOs.Result<DTOs.RelativityScript> readResult in relScriptReadResults.Results) { if (!readResult.Success) { Console.WriteLine(" An error occurred in read request: {0}", readResult.Message); Relativity | Services API Guide - 190 } } return false; } relScriptDTO = relScriptReadResults.Results[0].Artifact; // STEP 4: Retrieve the body as XML and output the name. XmlDocument bodyXml = relScriptDTO.Body; Console.WriteLine("Successfully read the Relativity Script '{0}'!", relScriptDTO.Name); return true; } 6.15.2 Querying for a RelativityScript This code sample illustrates how to set query conditions, call the Query() method on the RelativityScript repository, and iterate through the result set. public static bool Query_RelativityScript_Using_Repository(IRSAPIClient proxy) { // STEP 1: Create the query criteria. TextCondition nameCondition = new TextCondition (DTOs.RelativityScriptFieldNames.Name, TextConditionEnum.Like, "Billing Statistics"); DTOs.Query<DTOs.RelativityScript> relScriptQuery = new DTOs.Query<DTOs.RelativityScript> { Condition = nameCondition, Fields = DTOs.FieldValue.AllFields }; // STEP 2: Attempt to query for the RelativityScript. DTOs.QueryResultSet<DTOs.RelativityScript> relScriptQueryResults = null; try { relScriptQueryResults = proxy.Repositories.RelativityScript.Query (relScriptQuery); } catch (Exception ex) { Console.WriteLine("An error occurred querying for Relativity Scripts: {0} ", ex.Message); Relativity | Services API Guide - 191 return false; } // STEP 3: Check for success. if (!relScriptQueryResults.Success) { Console.WriteLine("An error occurred querying for Relativity Scripts: {0} ", relScriptQueryResults.Message); return false; } // STEP 4: Display the results. Console.WriteLine("Number of Relativity Scripts returned: {0}", relScriptQueryResults.Results.Count); foreach (DTOs.Result<DTOs.RelativityScript> relScriptResult in relScriptQueryResults.Results) { DTOs.RelativityScript curRelScript = relScriptResult.Artifact; Console.WriteLine("Relativity Script Name: {0}", curRelScript.TextIdentifier); Console.WriteLine("Relativity Script ArtifactID: {0}", curRelScript.ArtifactID); } return true; } 6.15.3 Executing a RelativityScript You can use the ExecuteRelativityScript() method to run a Relativity script by passing in the script ID, and the script input as a string. (You can perform a query to obtain the script ID.) The ExecuteRelativityScript() method may return a list of Artifacts and Fields, or the status of database rows affected by the script. You must be a member of the Relativity Script Admin group to use this method. The script should be configured with a return type of Table and have an integer column named ArtifactID. For each row in the output table, an Artifact object will be created with the ArtifactID from the ArtifactID column, and a set of Fields will be created for the remaining columns. The column names will be the Field names. When you execute a Relativity script, you can pass values for input parameters that are defined in the script body. In addition to the standard script properties, the following script called Script for Sample Project includes the input parameter _identifier that is named ControlNumber. For more information, see Script development on the Relativity 8.1 Developers site. <script> <name>Script for Sample Project</name> Relativity | Services API Guide - 192 <description></description> <category></category> <input> <constant id="_identifier " name="ControlNumber" type="text" /> </input> <action returns="table"><![CDATA[ SELECT ArtifactID, ControlNumber, ExtractedText FROM [Document] WHERE ControlNumber = #_identifier#]]></action> </script> The following code sample illustrates how to pass a value for a script parameter when running a script by calling the ExecuteRelativityScript() method on a RelativityScript DTO Repository object. public static bool ExecuteRelativityScript(IRSAPIClient proxy) { // STEP 1: Query by name for the script that you want to run. // Alternatively, if you have the ArtifactID, you can create a // RelativityScript object without querying as exemplified here: // DTOs.RelativityScript script = new DTOs.RelativityScript(1036254); DTOs.RelativityScript script; TextCondition nameCondition = new TextCondition(DTOs.RelativityScriptFieldNames.Name, TextConditionEnum.EqualTo, "Script For Sample Project"); DTOs.Query<DTOs.RelativityScript> relScriptQuery = new DTOs.Query<DTOs.RelativityScript> { Condition = nameCondition, Fields = DTOs.FieldValue.NoFields }; try { DTOs.QueryResultSet<DTOs.RelativityScript> relScriptQueryResults = null; relScriptQueryResults = proxy.Repositories.RelativityScript.Query (relScriptQuery); if (!relScriptQueryResults.Success) { Console.WriteLine(string.Format("An error occurred finding the script: {0}", relScriptQueryResults.Message)); return false; } Relativity | Services API Guide - 193 script = relScriptQueryResults.Results[0].Artifact; } catch (Exception ex) { Console.WriteLine("An error occurred querying for Relativity Scripts: {0} ", ex.Message); return false; } // STEP 2: Set the input parameter used in the Relativity Script. // The following sample Relativity Script contains an input parameter called "ControlNumber": // <input> // <constant id="_identifier" name="ControlNumber" type="text" /> // </input> RelativityScriptInput input = new RelativityScriptInput("ControlNumber", "AS000005"); List<RelativityScriptInput> inputList = new List<RelativityScriptInput> { input }; // STEP 3: Call the script. RelativityScriptResult scriptResult = null; try { scriptResult = proxy.Repositories.RelativityScript.ExecuteRelativityScript(script, inputList); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } //Check for success. if (!scriptResult.Success) { Console.WriteLine(string.Format(scriptResult.Message)); return false; } else { Int32 observedOutput = scriptResult.Count; Console.WriteLine(string.Format("Number of documents returned: {0}", observedOutput)); Relativity | Services API Guide - 194 foreach (Artifact art in scriptResult.Artifacts) { Console.WriteLine(string.Format("{0}: {1}", art.ArtifactID, art.Fields [1].Value)); } } return true; } 6.15.4 Retrieving Input for a RelativityScript You can use the GetRelativityScriptInputs() method to retrieve a list of input data for a script. An individual input parameter is represented by an instance of the RelativityScriptInputDetails class. You must be a member of the Relativity Script Admin group to use the GetRelativityScriptInputs() method. The following code sample illustrates how to use this method. public static IList<RelativityScriptInputDetails> GetRelativityScriptInputs (IRSAPIClient proxy) { List<RelativityScriptInputDetails> scriptInputList = null; . // STEP 1: Using ArtifactID, set the script you want to run. DTOs.RelativityScript script = new DTOs.RelativityScript(1036254); // STEP 2: Call GetRelativityScriptInputs. try { scriptInputList = proxy.Repositories.RelativityScript.GetRelativityScriptInputs(script); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return scriptInputList; } // STEP 3: Each RelativityScriptInputDetails object can be used to generate a RelativityScriptInput object, // but this example only displays information about each input. foreach (RelativityScriptInputDetails relativityScriptInputDetails in scriptInputList) { Console.WriteLine("Input Name: {0}\nInput Id: {1}\nInput Type: {2}\nInput Is Required: {3}\n", Relativity | Services API Guide - 195 relativityScriptInputDetails.Name, relativityScriptInputDetails.Id, relativityScriptInputDetails.InputType, relativityScriptInputDetails.IsRequired); } return scriptInputList; } 6.16 Tab You select tabs to access Relativity features, such as documents, RDOs, or applications. You can also use tabs as links to custom pages and other external resources. For more information, see Tabs in the Relativity 8.1 Documentation site, and Custom pages in the Relativity 8.1 Developers site. The Services API supports read and query operations on a Tab DTO. 6.16.1 Reading a Tab To read Field values on a Tab, you can use the Read() method on the Tab repository as illustrated in this code sample. public static bool Read_Tab(IRSAPIClient proxy) { // STEP 1: Call the Read() method on the Tab repository. DTOs.Tab requestedTab = new DTOs.Tab(1034254); requestedTab.Fields = DTOs.FieldValue.AllFields; DTOs.ResultSet<DTOs.Tab> results = null; try { results = proxy.Repositories.Tab.Read(requestedTab); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } //Check for success. if (!results.Success) { Console.WriteLine("An error occurred!"); Console.WriteLine(results.Message); return false; } Relativity | Services API Guide - 196 // STEP 2: Get the Artifact from the read results. DTOs.Tab tab = results.Results[0].Artifact; Console.WriteLine(">>>" + tab.ToString() + "<<<"); return true; } 6.16.2 Querying for a Tab This code sample illustrates how to set query conditions, call the Query() method on the Tab repository, and iterate through the result set. public static bool Query_Tabs(IRSAPIClient proxy) { // STEP 1: Setup your query criteria. DTOs.Query<DTOs.Tab> query = new DTOs.Query<DTOs.Tab>(); TextCondition parentCriteria = new TextCondition (DTOs.TabFieldNames.LinkType, TextConditionEnum.EqualTo, "Parent"); query.Condition = parentCriteria; query.Fields = DTOs.FieldValue.AllFields; // STEP 2: Call the query method on the Tab Repository. DTOs.QueryResultSet<DTOs.Tab> results = null; try { results = proxy.Repositories.Tab.Query(query); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } //Check for success. if (!results.Success) { Console.WriteLine("An error occurred!"); Console.WriteLine(results.Message); return false; } // STEP 3: Get the tabs from the query results. foreach (DTOs.Result<DTOs.Tab> tabResult in results.Results) Relativity | Services API Guide - 197 { DTOs.Tab tab = tabResult.Artifact; Console.WriteLine(tab.Name + " is a Parent tab."); } return true; } 6.17 User Individuals are added to Relativity as users, who are then added to groups associated with workspaces. For more information, see Users in the the Relativity 8.1 Documentation site. The Services API supports all CRUD and query operations on a User DTO. 6.17.1 Creating a User You can add a User to Relativity by calling the Create() method on the User repository. For more information about Password fields, see Fields used by Group and User objects on page 144. This code sample illustrates how to create the User DTO and references several utility methods for retrieving ArtifactIDs. See Utility methods used in User DTO creation on page 201. public static bool CreateUserUsingRepository(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = -1; int int int int int int defaultSelectedFileType = 1; userType = 3; documentSkip = 1000003; skipDefaultPreference = 1000004; password = 1000005; sendNewPasswordTo = 1000006; // STEP 1: Get the ArtifactIDs for the required Choice, Group, and Client objects. int returnPasswordCodeID = FindChoiceArtifactID(proxy, sendNewPasswordTo, "Return"); int passwordCodeID = FindChoiceArtifactID(proxy, password, "Auto-generate password"); int documentSkipCodeID = FindChoiceArtifactID(proxy, documentSkip, "Enabled"); int documentSkipPreferenceCodeID = FindChoiceArtifactID(proxy, skipDefaultPreference, "Normal"); Relativity | Services API Guide - 198 int defaultFileTypeCodeID = FindChoiceArtifactID(proxy, defaultSelectedFileType, "Native"); int userTypeCodeID = FindChoiceArtifactID(proxy, userType, "Internal"); int everyoneGroupArtifactID = FindGroupArtifactID(proxy, "Everyone"); int clientArtifactID = FindClientArtifactID(proxy, "Relativity Template"); // STEP 2: Create a User DTO for the User that you want to create. kCura.Relativity.Client.DTOs.User userDTO = new kCura.Relativity.Client.DTOs.User(); userDTO.AdvancedSearchPublicByDefault = true; userDTO.AuthenticationData = ""; userDTO.BetaUser = false; userDTO.ChangePassword = true; userDTO.ChangePasswordNextLogin = false; userDTO.ChangeSettings = true; userDTO.Client = new kCura.Relativity.Client.DTOs.Client(clientArtifactID); userDTO.DataFocus = 1; userDTO.DefaultSelectedFileType = new kCura.Relativity.Client.DTOs.Choice (defaultFileTypeCodeID); userDTO.DocumentSkip = new kCura.Relativity.Client.DTOs.Choice (documentSkipCodeID); userDTO.EmailAddress = "zzz12344@test.com"; userDTO.EnforceViewerCompatibility = true; userDTO.FirstName = "Bruce"; userDTO.Groups = new List<kCura.Relativity.Client.DTOs.Group> { new kCura.Relativity.Client.DTOs.Group(everyoneGroupArtifactID) }; userDTO.ItemListPageLength = 25; userDTO.KeyboardShortcuts = true; userDTO.LastName = "User for User Create Testing"; userDTO.MaximumPasswordAge = 0; userDTO.NativeViewerCacheAhead = true; userDTO.PasswordAction = new kCura.Relativity.Client.DTOs.Choice (passwordCodeID); userDTO.RelativityAccess = true; userDTO.SendPasswordTo = new kCura.Relativity.Client.DTOs.Choice (returnPasswordCodeID); Relativity | Services API Guide - 199 userDTO.SkipDefaultPreference = new kCura.Relativity.Client.DTOs.Choice (documentSkipPreferenceCodeID); userDTO.TrustedIPs = ""; userDTO.Type = new kCura.Relativity.Client.DTOs.Choice(userTypeCodeID); WriteResultSet<kCura.Relativity.Client.DTOs.User> createResults = new WriteResultSet<kCura.Relativity.Client.DTOs.User>(); // STEP 3: Attempt to create the User. try { createResults = proxy.Repositories.User.Create(userDTO); } catch (Exception ex) { Console.WriteLine(String.Format("An error occurred: {0}", ex.Message)); return false; } // Check for success. if (!createResults.Success) { Console.WriteLine(String.Format("An error occurred creating user: {0}", createResults.Message)); foreach (Result<kCura.Relativity.Client.DTOs.User> createResult in createResults.Results) { if (!createResult.Success) { Console.WriteLine(String.Format(" An error occurred in create request: {0}", createResult.Message)); } } return false; } //STEP 4: Output the password. Console.WriteLine(String.Format("Password for created user is {0}", createResults.Results[0].Artifact["Password"])); return true; } Relativity | Services API Guide - 200 6.17.2 Utility methods used in User DTO creation The code sample illustrating how to create the User DTO uses the following utility methods to facilitate the retrieval of ArtifactIDs for Choices, Groups, and Clients. FindChoiceArtifactID() method private static int FindChoiceArtifactID(IRSAPIClient proxy, int choiceType, string value) { int artifactID = 0; WholeNumberCondition choiceTypeCondition = new WholeNumberCondition(ChoiceFieldNames.ChoiceTypeID, NumericConditionEnum.EqualTo, (int)choiceType); TextCondition choiceNameCondition = new TextCondition(ChoiceFieldNames.Name, TextConditionEnum.EqualTo, value); CompositeCondition choiceCompositeCondition = new CompositeCondition(choiceTypeCondition, CompositeConditionEnum.And, choiceNameCondition); Query<kCura.Relativity.Client.DTOs.Choice> choiceQuery = new Query<kCura.Relativity.Client.DTOs.Choice>(new List<FieldValue> { new FieldValue(ArtifactQueryFieldNames.ArtifactID) }, choiceCompositeCondition, new List<Sort>()); try { QueryResultSet<kCura.Relativity.Client.DTOs.Choice> choiceQueryResult = proxy.Repositories.Choice.Query(choiceQuery); if (choiceQueryResult.Success && choiceQueryResult.Results.Count == 1) { artifactID = choiceQueryResult.Results.FirstOrDefault ().Artifact.ArtifactID; } else { Console.WriteLine("The choice could not be found."); } } catch (Exception ex) { Console.WriteLine(String.Format("An error occurred: {0}", ex.Message)); Relativity | Services API Guide - 201 } return artifactID; } FindGroupArtifactID() method private static int FindGroupArtifactID(IRSAPIClient proxy, string group) { int artifactID = 0; TextCondition groupCondition = new TextCondition(GroupFieldNames.Name, TextConditionEnum.EqualTo, group); Query<kCura.Relativity.Client.DTOs.Group> queryGroup = new kCura.Relativity.Client.DTOs.Query<kCura.Relativity.Client.DTOs.Group> { Condition = groupCondition }; queryGroup.Fields.Add(new FieldValue(ArtifactQueryFieldNames.ArtifactID)); try { QueryResultSet<kCura.Relativity.Client.DTOs.Group> resultSetGroup = proxy.Repositories.Group.Query(queryGroup, 0); if (resultSetGroup.Success && resultSetGroup.Results.Count == 1) { artifactID = resultSetGroup.Results.FirstOrDefault ().Artifact.ArtifactID; } else { Console.WriteLine("The Query operation failed.{0}{1}", Environment.NewLine, resultSetGroup.Message); } } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); } return artifactID; } FindClientArtifactID() method Relativity | Services API Guide - 202 private static int FindClientArtifactID(IRSAPIClient proxy, string group) { int artifactID = 0; TextCondition clientCondition = new TextCondition(ClientFieldNames.Name, TextConditionEnum.EqualTo, group); Query<kCura.Relativity.Client.DTOs.Client> queryClient = new Query<kCura.Relativity.Client.DTOs.Client> { Condition = clientCondition }; queryClient.Fields = FieldValue.AllFields; try { QueryResultSet<kCura.Relativity.Client.DTOs.Client> resultSetClient = proxy.Repositories.Client.Query(queryClient, 0); if (resultSetClient.Success && resultSetClient.Results.Count == 1) { artifactID = resultSetClient.Results.FirstOrDefault ().Artifact.ArtifactID; } else { Console.WriteLine("The Query operation failed.{0}{1}", Environment.NewLine, resultSetClient.Message); } } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); } return artifactID; } 6.17.3 Reading a User To read Field values, you can use the Read() method on the User repository as illustrated in this code sample. public static bool ReadUserUsingRepository(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = -1; Relativity | Services API Guide - 203 // STEP 1: Create the User DTO. kCura.Relativity.Client.DTOs.User user = new kCura.Relativity.Client.DTOs.User(1015464); user.Fields = FieldValue.AllFields; ResultSet<kCura.Relativity.Client.DTOs.User> userResults = null; //STEP 2: Read the User DTO. try { userResults = proxy.Repositories.User.Read(user); } catch (Exception ex) { Console.WriteLine("An error occurred reading the user: {0}", ex.Message); return false; } //STEP 3: Check for success. if (!userResults.Results.Any()) { Console.WriteLine("Could not find the user: {0}", userResults.Message); return false; } return true; } 6.17.4 Updating a User You can use the Update() method on the User repository to modify its properties as illustrated in this code sample. See Fields used by Group and User objects on page 144. public static bool UpdateUserUsingRepository(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = -1; //STEP 1: Define the query. Condition userQueryCondition = new TextCondition(UserFieldNames.FirstName, TextConditionEnum.EqualTo, "Bruce"); Query<kCura.Relativity.Client.DTOs.User> userQuery = new Query<kCura.Relativity.Client.DTOs.User>(FieldValue.AllFields, userQueryCondition, new List<Sort>()); QueryResultSet<kCura.Relativity.Client.DTOs.User> userQueryResults = null; Relativity | Services API Guide - 204 //STEP 2: Query for the User. try { userQueryResults = proxy.Repositories.User.Query(userQuery); } catch (Exception ex) { Console.WriteLine("An error occurred querying for the user: {0}", ex.Message); return false; } if (!userQueryResults.Results.Any()) { Console.WriteLine("Could not find the user: {0}", userQueryResults.Message); return false; } //STEP 3: Retreive the User artifact and update it. kCura.Relativity.Client.DTOs.User userToUpdate = userQueryResults.Results.First().Artifact; userToUpdate.FirstName = "Steve"; WriteResultSet<kCura.Relativity.Client.DTOs.User> userUpdateResults = null; //STEP 4: Complete the update. try { userUpdateResults = proxy.Repositories.User.Update(userToUpdate); } catch (Exception ex) { Console.WriteLine("An error occurred updating the user: {0}", ex.Message); return false; } //STEP 5: Check for success. if (userUpdateResults.Success) { Console.WriteLine("The user has been updated!"); } else { Relativity | Services API Guide - 205 Console.WriteLine("An error occurred updating the user: {0}", userUpdateResults.Message); return false; } return true; } 6.17.5 Deleting a User You can remove a User from Relativity by calling the Delete() method on the User repository as illustrated in this code sample. public static bool DeleteUserUsingRepository(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = -1; // STEP 1: Create the User DTO. kCura.Relativity.Client.DTOs.User user = new kCura.Relativity.Client.DTOs.User(1015464); ResultSet<kCura.Relativity.Client.DTOs.User> userResults = null; // STEP 2: Delete the User. try { userResults = proxy.Repositories.User.Delete(user); } catch (Exception ex) { Console.WriteLine("An error occurred deleting for the user: {0}", ex.Message); return false; } // STEP 3: Check for success. if (!userResults.Results.Any()) { Console.WriteLine("Could not delete the user: {0}", userResults.Message); return false; } return true; } 6.17.6 Querying for a User This code sample illustrates how to set query conditions, call the Query() method on the User repository, and iterate through the result set. Relativity | Services API Guide - 206 public static bool QueryUserUsingRepository(IRSAPIClient proxy) { proxy.APIOptions.WorkspaceID = -1; // STEP 1: Create the query for a given user. Condition userQueryCondition = new TextCondition(UserFieldNames.FirstName, TextConditionEnum.EqualTo, "Bruce"); Query<kCura.Relativity.Client.DTOs.User> userQuery = new Query<kCura.Relativity.Client.DTOs.User>(FieldValue.AllFields, userQueryCondition, new List<Sort>()); QueryResultSet<kCura.Relativity.Client.DTOs.User> userQueryResults = null; // STEP 2: Query for the user. try { userQueryResults = proxy.Repositories.User.Query(userQuery); } catch (Exception ex) { Console.WriteLine("An error occurred querying for the user: {0}", ex.Message); return false; } // STEP 3: Check for success. if (!userQueryResults.Results.Any()) { Console.WriteLine("Could not find the user: {0}", userQueryResults.Message); return false; } return true; } 6.18 View Relativity uses views to provide customizable item lists. For more information, see Views in the Relativity 8.1 Documentation site. The Services API supports read and query operations on a View DTO. Relativity | Services API Guide - 207 6.18.1 Reading a View To read Field values on a View, you can use the Read() method on the View repository as illustrated in this code sample. public static bool Read_View_Using_Repository(IRSAPIClient proxy) { // STEP 1: Create a View DTO for the View to be read. DTOs.View viewDTO = new DTOs.View(1034249); viewDTO.Fields = FieldValue.AllFields; ResultSet<DTOs.View> viewReadResults = new ResultSet<DTOs.View>(); // STEP 2: Try to read the View. try { viewReadResults = proxy.Repositories.View.Read(viewDTO); } catch (Exception ex) { Console.WriteLine("An error occurred reading the view: {0}", ex.Message); return false; } // STEP 3: Check for success. if (!viewReadResults.Success) { Console.WriteLine("An error occurred reading the view: {0}", viewReadResults.Message); foreach (Result<DTOs.View> readResult in viewReadResults.Results) { if (!readResult.Success) { Console.WriteLine(" An error occurred in read request: {0}", readResult.Message); } } return false; } // STEP 4: Output the name. Console.WriteLine("Successfully read the view '{0}'!", viewReadResults.Results[0].Artifact.Name); return true; } Relativity | Services API Guide - 208 6.18.2 Querying for a View This code sample illustrates how to set query conditions, call the Query() method on the View repository, and iterate through the result set. public static bool Query_View_Using_Repository(IRSAPIClient proxy) { // STEP 1: Create the Query criteria. ObjectsCondition relAppCondition = new ObjectsCondition("Relativity Applications", ObjectsConditionEnum.AllOfThese, new int[] { 1035699 }); Query<DTOs.View> viewQuery = new Query<DTOs.View> { Condition = relAppCondition, Fields = FieldValue.AllFields }; // STEP 2: Try to query for the View. QueryResultSet<DTOs.View> viewQueryResults = null; try { viewQueryResults = proxy.Repositories.View.Query(viewQuery); } catch (Exception ex) { Console.WriteLine("An error occurred querying for views: {0}", ex.Message); return false; } // STEP 3: Check for success. if (!viewQueryResults.Success) { Console.WriteLine("An error occurred querying for views: {0}", viewQueryResults.Message); return false; } // STEP 4: Display the results. Console.WriteLine("Number of views returned: {0}", viewQueryResults.Results.Count); foreach (Result<DTOs.View> viewResult in viewQueryResults.Results) { Relativity | Services API Guide - 209 DTOs.View curView = viewResult.Artifact; Console.WriteLine("View Name: {0}", curView.TextIdentifier); Console.WriteLine("View ArtifactID: {0}", curView.ArtifactID); } return true; } 6.19 Workspace In Relativity, workspaces are secure data repositories for storing documents and applications. For additional information, see Workspaces in the Relativity 8.1 Documentation site. The Services API supports read and query operations on a Workspace DTO. 6.19.1 Reading a Workspace To read Field values on a Workspace, you can use the Read() method on the Workspace repository as illustrated in this code sample. public static bool Read_Workspace(IRSAPIClient proxy) { // STEP 1: Create ResultSet to store Results. ResultSet<DTOs.Workspace> resultSet = new ResultSet<DTOs.Workspace>(); // STEP 2: Perform the Read operation. // You could create a DTOs.Workspace object, set its ArtifactID property, // and use the Workspace object as the parameter. However, this example // demonstrates using the ArtifactID as a parameter. try { resultSet = proxy.Repositories.Workspace.Read(1016204); } catch (Exception ex) { Console.WriteLine("An error occurred: {0}", ex.Message); return false; } // Check for success. if (!resultSet.Success) { Console.WriteLine("The Read operation failed.{0}{1}", Environment.NewLine, resultSet.Message); return false; Relativity | Services API Guide - 210 } // Display results. Console.WriteLine("Read completed successfully."); foreach (DTOs.Result<DTOs.Workspace> workspaceResult in resultSet.Results) { Console.WriteLine(String.Format("{0}Name:{1}", Environment.NewLine, workspaceResult.Artifact.Name)); Console.WriteLine(String.Format("ArtifactID:{0}", workspaceResult.Artifact.ArtifactID)); } return true; } 6.19.2 Querying for a Workspace This code sample illustrates how to set query conditions, call the Query() method on the Workspace repository, and iterate through the result set. public static bool Query_Workspace(IRSAPIClient proxy) { //STEP 1: Create a Query and WholeNumberCondition. WholeNumberCondition workspaceCondition = new WholeNumberCondition(ArtifactQueryFieldNames.ArtifactID, NumericConditionEnum.EqualTo, 1016204); Query<DTOs.Workspace> query = new DTOs.Query<DTOs.Workspace> { Condition = workspaceCondition }; query.Fields = FieldValue.AllFields; //STEP 2: Create QueryResultSet to collect information about the DTO after Query. QueryResultSet<DTOs.Workspace> resultSet = new QueryResultSet<DTOs.Workspace>(); //STEP 3: Perform the Query. try { resultSet = proxy.Repositories.Workspace.Query(query, 0); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); Relativity | Services API Guide - 211 return false; } //Check for success. if (!resultSet.Success) { Console.WriteLine("The Query operation failed.{0}{1}", Environment.NewLine, resultSet.Message); return false; } // Display the results. Console.WriteLine(string.Format("Number of Workspaces returned: {0}", resultSet.Results.Count)); foreach (DTOs.Result<DTOs.Workspace> workspaceResult in resultSet.Results) { Console.WriteLine(string.Format("{0}Name:{1}", Environment.NewLine, workspaceResult.Artifact.Name)); Console.WriteLine(string.Format("ArtifactID:{0}", workspaceResult.Artifact.ArtifactID)); } return true; } 7 Additional Services API functionality The Services API includes additional features for querying, file transfers, mass operations, and others. 7.1 File transfers Through the Services API, you can perform download and upload requests on a File field as well as clear the field. 7.1.1 Supported file transfer operations The RSAPIClient supports the following operations for file transfers: n Download () - The following overloaded methods are available: o Download(FileRequest) - downloads the contents of a Relativity file field and returns it in the form of a Stream object. It takes a FileRequest and returns an instance of DownloadResponse. It raises the DownloadComplete event when the operation is successful. o Download(FileRequest, String) - accepts a FileRequest, and a String which represents the destination location of the file on disk. Relativity | Services API Guide - 212 Note: You can also download files by using the URL retrieved from a File field on a Dynamic Object. See Getting a download URL for a File field on page 139. n n Upload () - takes an UploadRequest. It raises the UploadComplete event when the operation is successful. The Upload() and Download() methods support zero-length files. Clear () - clears the value of a file field in Relativity. It returns no response when it completes successfully. When an operation fails on these methods, a Failure event is raised that contains instance of FailureEventArgs class. This instance contains the exception that would have been thrown during a failed operation. See RSAPIClient events in the Services API class library. 7.1.2 File transfer error messages The following table lists error classes associated with various operations related to file transfers. Operation All Operations Error Classes FileTransferFault InvalidFieldIdFault InvalidObjectArtifactIdFault InvalidWorkspaceIdFault MissingFileFieldFault FileSizeMismatchFault NoOverwriteFault OperationInProgressfault EmptyFieldFault No error messages Upload() Download() Clear() 7.1.3 Error classes In the Services API, you can use error classes to obtain additional information for troubleshooting. Each error class uses one or more dedicated messages. The returned exception object has as a property named Message. The following table lists the error classes as well as their associated messages and possible causes. Class EmptyFieldFault FileSizeMismatchFault FileTransferFault FileTransferFault Message Field is empty, no file to download. Size specified in request does not match size of file received. Cause Field contains no file. Number of bytes written to disk by the server doesn't match the number specified in the UploadRequest.Metadata.FileSize property. Request could not be authenToken supplied doesn't refer to a ticated. logged-in session Cannot download chunk index [X] An invalid chunk index was specified for file, there are only [Z] chunks during the download operation. total. Relativity | Services API Guide - 213 Class FileTransferFault FileTransferFault InvalidFieldIdFault InvalidObjectArtifactIdFault InvalidWorkspaceIdFault MissingFileFieldFault NoOverwriteFault OperationInProgressFault Message Invalid sessionId. Cause SessionId supplied during a transfer operation doesn't refer to an existing transfer session. Unable to parse sessionId. SessionId supplied during a transfer operation is in an invalid format. FieldId is invalid. The Target.FieldId property supplied is <= 0. ObjectArtifactId is invalid. The Target.ObjectArtifactId property supplied is <= 0. WorkspaceId is invalid. The Target.WorkspaceId property supplied is <= 0. No file field with id [X] could be The Target.FieldId property supplied found. doesn't refer to an existing field. Field with existing value not over- The file field referred to by Tarwritten. get.FieldId isn't empty but UploadRequest. Overwrite is set to False. An upload operation is in progress An upload operation is currently in proalready for the object with id [X] in gress on the specified field in the workthe workspace with id [Z]. space. 7.1.4 Sample error handling code Use the following guidelines when handling errors: n n n Listen for the Failure event on the RSAPIClient class. See RSAPIClient.Failure Event in the Services API class library. Pull the exception thrown during a transfer operation from the FailureEventArgs.Exception property. Inspect the type of the exception so you can take the appropriate action. All of the FaultException errors thrown by the RSAPIClient service are caught, as well as any .NET framework exceptions. The exception isn't always a FaultException<T>. The following sample illustrates a basic approach to error handling in the Services API: private void HandleFailureEvents(FalureEventArgs failureInfo) { Exception excepDetail = failureInfo.Exception; if (excepDetail is FaultException<OperationInProgressFault>) { Interaction.MsgBox(excepDetail > Message, MsgBoxStyle.Critical, "Error!"); } else { Interaction.MsgBox("An error occurred!", MsgBoxStyle.Critical, "Error"); } } Relativity | Services API Guide - 214 7.2 Mass processes The Services API provides you with the ability to perform an operation on multiple items with a single API call. 7.2.0.1 Using Lists of Artifacts as input to CRUD methods Each of the CRUD methods takes a List of Artifacts so you can perform these operations on multiple objects in single call. Use the following guidelines when submitting ArtifactRequests for CRUD operations: n n n You can pass ArtifactRequests of different types. For example, you could submit a list of ArtifactRequests to the Update() method that contains Artifacts for Documents and Dynamic Objects. You must pass all the parameters required by the CRUD method that you are using for each ArtifactRequest. For example, you must pass the ArtifactType and list of Fields for each ArtifactRequest when creating Artifacts. No transactional guarantee exists for the method as a whole. The creation, update or deletion of each ArtifactRequest is handled individually. If the operation against a single ArtifactRequest fails, the Success flag on the Result object for the corresponding ArtifactRequest is set to False, and returned in the ResultSet. The operations on the other ArtifactRequests will proceed. 7.2.0.2 MassCreate() and MassCreateWithDetails() methods You can use the MassCreate() and MassCreateWithDetails() methods to create multiple Dynamic Objects. Both methods use a template to minimize the repetition of Field values that are common to all the Artifacts. The MassCreateWithDetails() method also populates the Results property with success and failure information as part of the object creation process. This information is provided even when a partial failure occurs. The MassCreate() and MassCreateWithDetails() methods take the Fields specified for each Artifact included in the List of Artifacts passed as a parameter. View sample code for the MassCreate() method public static bool Using_MassCreate(IRSAPIClient proxy) { // STEP 1: Create Artifact that serves as a template for the RDOs that you want to create. ArtifactRequest artifactRequest = new ArtifactRequest("Employees"); List<Field> templateFields = new List<Field>(); templateFields.Add(new templateFields.Add(new templateFields.Add(new artifactRequest.Fields Field("Name")); Field("Last Name")); Field("Employment Level", 1036226)); = templateFields; // STEP 2: Create a list of Artifacts of RDOs to create via MassCreate. List<ArtifactRequest> artifactRequestList = new List<ArtifactRequest>(); for (int i = 1; i <= 10; i++) { ArtifactRequest anotherArtifactRequest = new ArtifactRequest(); Relativity | Services API Guide - 215 List<Field> fields = new List<Field>(); fields.Add(new Field("Name", i.ToString())); fields.Add(new Field("Last Name", "Employee #:" + i.ToString())); anotherArtifactRequest.Fields = fields; anotherArtifactRequest.ParentArtifactID = 1016204; // The identifier 1016204 is the WorkspaceID. artifactRequestList.Add(anotherArtifactRequest); } // STEP 3: Call the Services API to MassCreate the Employees. MassCreateResult results = new MassCreateResult(); try { results = proxy.MassCreate(proxy.APIOptions, artifactRequest, artifactRequestList); Console.WriteLine(string.Format("MassCreate Success Flag: {0}", results.Success)); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } if (!results.Success) { Console.WriteLine(results.Message); foreach (Result r in results.Results) { Console.WriteLine(r.Message); } return false; } return true; } View sample code for the MassCreateWithDetails() method public static bool Using_MassCreateWithDetails(IRSAPIClient proxy) { Relativity | Services API Guide - 216 // STEP 1: Create Artifact that serves as a template for the RDOs that you want to create. ArtifactRequest artifactRequest = new ArtifactRequest("Employees"); List<Field> templateFields = new List<Field>(); templateFields.Add(new Field("Name")); templateFields.Add(new Field("Last Name")); templateFields.Add(new Field("Employment Level", 1036226)); artifactRequest.Fields = templateFields; // STEP 2: Create a list of Artifacts of the RDOs created via MassCreate. List<ArtifactRequest> artifactRequestList = new List<ArtifactRequest>(); for (int i = 1; i <= 10; i++) { ArtifactRequest anotherArtifactRequest = new ArtifactRequest(); List<Field> fields = new List<Field>(); fields.Add(new Field("Name", i.ToString())); fields.Add(new Field("Last Name", "Employee #:" + i.ToString())); anotherArtifactRequest.Fields = fields; anotherArtifactRequest.ParentArtifactID = 1016204; // The identifier 1016204 is the WorkspaceID. artifactRequestList.Add(anotherArtifactRequest); } // STEP 3: Call the Services API to MassCreate the Employees. MassCreateResult results = new MassCreateResult(); try { results = proxy.MassCreateWithDetails(proxy.APIOptions, artifactRequest, artifactRequestList); Console.WriteLine(string.Format("MassCreate Success Flag: {0}", results.Success)); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } // The results objects contains the Artifact ID for each new Artifact. if (results.Success) { Console.WriteLine("Total Artifacts Created: " + results.Count); Relativity | Services API Guide - 217 foreach (Result result in results.Results) { Console.WriteLine("Created Artifact: " + result.ArtifactID); } } else { Console.WriteLine(results.Message); foreach (Result r in results.Results) { Console.WriteLine(r.Message); } return false; } //Clean up. try { artifactRequestList = new List<ArtifactRequest>(); for (int i = 1; i <= 10; i++) { ArtifactRequest anotherArtifactRequest = new ArtifactRequest("Employees", results.Results[i-1].ArtifactID); artifactRequestList.Add(anotherArtifactRequest); } proxy.Delete(proxy.APIOptions, artifactRequestList); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } return true; } The mass create methods are subject to the following restrictions: n n n n Only Dynamic Objects can be created with this method. All Artifacts to be created must be of the same ArtifactType, and all Fields must be specified by Name. Don't use the ArtifactID. Each Field in the template must have a value, and be specified by Name. Don't use NULL values or specify Fields by ArtifactID. All required Fields must have values. Note: The Configuration Table contains the MaxNumberOfArtfactsToMassCreate setting, which controls the maximum number of Dynamic Objects that can be create with this method. The default value is currently 1,000,000. Relativity | Services API Guide - 218 7.2.0.3 MassEdit() method You can use the MassEdit() method to apply the same set of Field updates to multiple Documents in a single call. This method uses a template Document as an ArtifactRequest, which determines how the call updates other documents. It also takes a list of ArtifactIDs that identify the Documents for editing. You can only update Documents with this method, and updates aren't propagated to related documents as they are in the Relativity web UI. Note: The Configuration Table contains the MaxNumberOfArtfactsToMassEdit setting, which controls maximum number of documents edited in a single call. The default value is 1,000,000. In addition, the MassEdit() method updates the documents in batches. MassEditBatchAmount is the configuration setting that controls the number of documents in each batch, and it is also used by the Relativity web UI. The default and recommended value is 1,000. View sample code for the MassEdit() method public static bool Using_MassEdit(IRSAPIClient proxy) { // STEP 1: Create Artifact that serves as a template for the Documents that you want to update. List<Field> fields = new List<Field>(); fields.Add(new Field("MD5 Hash", "New Value")); ArtifactRequest artifactRequest = new ArtifactRequest("Document"); artifactRequest.Fields = fields; // STEP 2: Create a list of ArtifactIDs of the Documents updated via MassEdit. List<Int32> artifactIDsToUpdate = new List<Int32>() {1035607, 1035608}; //STEP 3: Call the Services API to MassEdit the Documents. MassEditResult resultSet; try { resultSet = proxy.MassEdit(proxy.APIOptions, artifactRequest, artifactIDsToUpdate); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } Console.WriteLine(string.Format("MassEdit Success Flag: {0}", resultSet.Success)); Relativity | Services API Guide - 219 // Check for success. if (!resultSet.Success) { Console.WriteLine(resultSet.Message); return false; } return true; } 7.2.0.4 ExecuteBatch() method You can use the ExecuteBatch() method to combine multiple operations in a single database transaction. For example, you could create a set of Artifacts and update a set of Documents using a single round-trip to the Services API. View sample code for the ExecuteBatch() method public static bool Using_ExecuteBatch(IRSAPIClient proxy) { // STEP 1: Create RDOs used later to demonstrate the ExecuteBatch() method. ArtifactRequest artifactRequest1 = new ArtifactRequest(); artifactRequest1.ParentArtifactID = 1016204; artifactRequest1.ArtifactTypeName = "Employees"; artifactRequest1.Fields = new List<Field>(); Field nameField1 = new Field("Name", "John"); Field lastNameField1 = new Field("Last Name", "Doe"); artifactRequest1.Fields.Add(nameField1); artifactRequest1.Fields.Add(lastNameField1); ArtifactRequest artifactRequest2 = new ArtifactRequest(); artifactRequest2.ParentArtifactID = 1016204; artifactRequest2.ArtifactTypeName = "Employees"; artifactRequest2.Fields = new List<Field>(); Field nameField2 = new Field("Name", "Jane"); Field lastNameField2 = new Field("Last Name", "Smith"); artifactRequest2.Fields.Add(nameField2); artifactRequest2.Fields.Add(lastNameField2); List<ArtifactRequest> artifactRequestList = new List<ArtifactRequest>(); artifactRequestList.Add(artifactRequest1); artifactRequestList.Add(artifactRequest2); ResultSet results = new ResultSet(); Relativity | Services API Guide - 220 // Attempt to create the new Users. try { results = proxy.Create(proxy.APIOptions, artifactRequestList); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } List<ArtifactRequest> artifactRequestToReadList = new List<ArtifactRequest> (); foreach (Result result in results.Results) { ArtifactRequest artifactRequestToRead = new ArtifactRequest(); artifactRequestToRead.ArtifactID = result.ArtifactID; artifactRequestToRead.ArtifactTypeID = 1000036; artifactRequestToReadList.Add(artifactRequestToRead); } ReadResultSet readResultSet = new ReadResultSet(); //Read back the Users just created. try { readResultSet = proxy.Read(proxy.APIOptions, artifactRequestToReadList); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } List<Artifact> sampleEmployees = new List<Artifact>(); foreach (ReadResult readResult in readResultSet.ReadResults) { sampleEmployees.Add(readResult.Artifact); } ArtifactRequest john = new ArtifactRequest((Int32)sampleEmployees[0].ArtifactTypeID, (Int32) sampleEmployees[0].ArtifactID); ArtifactRequest jane = Relativity | Services API Guide - 221 new ArtifactRequest((Int32)sampleEmployees[1].ArtifactTypeID, (Int32) sampleEmployees[1].ArtifactID); // STEP 2: Create an Update Command for one artifact. List<ArtifactRequest> artifactRequestsToUpdate = new List<ArtifactRequest> { john }; Field lastNameField = sampleEmployees[0].Fields.FirstOrDefault(field => field.Name == "Last Name"); lastNameField.Value = "Doe - Updated"; john.Fields.Add(lastNameField); // Add a new skill to this employee by updating the Skills Inventory multichoice field. Field skills = sampleEmployees[0].Fields.FirstOrDefault(field => field.Name == "Skills Inventory"); // Define the skill to be added as a MultiChoice Update Value that is 'merged' with any existing skill values. MultiChoiceUpdateValue skillToBeAdded = new MultiChoiceUpdateValue(); skillToBeAdded.Value = new List<Int32>(); skillToBeAdded.Value.Add(1036219); skillToBeAdded.Behavior = MultiChoiceUpdateBehavior.Merge; // Set the Skills field value to be that of the MultiChoiceUpdateValue. skills.Value = skillToBeAdded; john.Fields.Add(skills); Command anUpdateCommand = new UpdateCommand(artifactRequestsToUpdate); //STEP 3: Create a Delete Command for an artifact. List<ArtifactRequest> artifactRequestsToDelete = new List<ArtifactRequest> (); artifactRequestsToDelete.Add(jane); Command aDeleteCommand = new DeleteCommand(artifactRequestsToDelete); // STEP 4: Bundle the Command objects into a list. The commands are executed in the order that they are provided. List<Command> commands = new List<Command>(); commands.Add(anUpdateCommand); commands.Add(aDeleteCommand); ExecuteBatchResultSet executeBatchResults = new ExecuteBatchResultSet(); // STEP 5: Call the Services API to execute the batch of Commands. try { Relativity | Services API Guide - 222 executeBatchResults = proxy.ExecuteBatch(proxy.APIOptions, commands, TransactionType.Batch); Console.WriteLine(string.Format("Batch Success Flag: {0}", results.Success)); if (!results.Success) { foreach (ResultSet resultSetItem in executeBatchResults.ResultSets) { Console.WriteLine(resultSetItem.ResultSetType.ToString() + ": " + resultSetItem.Success.ToString()); foreach (Result resultItem in resultSetItem.Results) { if (!resultItem.Success) Console.WriteLine(resultItem.Message); } } return false; } } catch (Exception ex) { Console.WriteLine("There was an error:" + ex.Message); } return true; } Use the following guidelines when calling this method: n n n Input to the ExecuteBatch() method is a List of Command objects. All operations execute within a database transaction. If an error occurs, the entire batch is rolled-back. Executing a large number of create, update, and delete operations may take considerable time and result in table locking. Note: The Configuration Table contains the MaxArtifactBatchSizeForExecuteBatch setting, which controls the number of Artifacts processed in a single method call. The default value is 2000, but you can update it as necessary. This setting has been implemented to limit the length of time that database transaction opened by ExecuteBatch() method holds locks on tables, preventing Relativity users from experiencing errors. 7.2.1 Mass delete operations Using the Services API, you can perform mass delete operations on Documents and their associated files, as well as on other object types. The account used to perform these operations must be a Relativity administrator with Delete Object Dependencies permissions. See Security permissions on the Relativity 8.1 Documentation site. Relativity | Services API Guide - 223 The methods, classes, and the enumeration used to perform mass delete operations are available in the kCura.Relativity.Client namespace. Note: For the code samples provided on this page, you can assume that the APIOptions object on the proxy has the appropriate token and WorkspaceID. 7.2.1.1 MassDeleteOptions and DocumentMassDeleteOptions classes When calling the mass delete methods, you need to pass an instance of the MassDeleteOptions or DocumentMassDeleteOptions class. These classes provides information about how to delete objects. DocumentMassDeleteOptions class You pass an instance of the DocumentMassDeleteOptions class to the MassDeleteDocuments() or MassDeleteAllDocuments() methods. To indicate how to perform the deletion, you can set either or both of the following fields on the DocumentMassDeleteOptions class to true or false: n n Force Delete – removes the selected Documents even if they have redactions, annotations, links, or tags. Cascade Delete – removes the selected Documents, as well as deletes all child objects and unlinks associative objects. In addition, you can use the DeleteType enumeration to specify the type of files associated with the Documents that you want to delete: n n n n AllAssociatedFiles – indicates that you want to delete Documents and all dependent files, including images and native files. It also results in the deletion of field values. Images – indicates that you want to delete only the images associated with the selected Documents. Natives – indicates that you want to delete only the native files associated with the selected Documents. ImagesAndNatives – indicates that you want to delete only the images and native files associated with the selected Documents. (The field values for the documents aren't deleted.) MassDeleteOptions class You pass an instance of the MassDeleteOptions class to the MassDelete() or MassDeleteAllObjects methods. It indicates the type of the object that you want to delete, as well as whether you want to delete all dependent objects by performing a cascade delete. You can set the Cascade Delete field to true when you want to delete all child objects and unlink associative objects. 7.2.1.2 MassDeleteAllDocuments() method You can use the MassDeleteAllDocuments() to delete all Documents and their associated files in the Workspace. This code sample illustrates how to set the CascadeDelete and ForceDelete fields on the DocumentMassDeleteOptions object when deleting all Documents and their associated files in a Workspace. public static bool Using_MassDelete_ ToCascadeAndForceDeleteAllDocumentsInWorkspace(IRSAPIClient proxy) { //STEP 1: Set up delete options. Relativity | Services API Guide - 224 var deleteOptions = new DocumentMassDeleteOptions (DocumentMassDeleteOptions.DeleteType.AllAssociatedFiles); deleteOptions.CascadeDelete = true; deleteOptions.ForceDelete = true; //STEP 2: Delete all documents. var result = proxy.MassDeleteAllDocuments(proxy.APIOptions, deleteOptions); //STEP 3: Check for success. if (!result.Success) { Console.WriteLine(result.Message); return false; } return true; } 7.2.1.3 MassDeleteDocuments() method You can use the MassDeleteDocuments() method to delete all files , images, natives, or both the images and natives associated with a Document. You must use a DeleteType enum to specify the file types that you want to delete, and then list the ArtifactID of each Document. This code sample illustrates how to force delete images for the Documents with the specified ArtifactIDs. A force deletion removes even Documents containing redactions and annotations from the Workspace. public static bool Using_MassDelete_ToForceDeleteImagesForSpecificDocuments (IRSAPIClient proxy) { //STEP 1: Set up delete options. var deleteOptions = new DocumentMassDeleteOptions (DocumentMassDeleteOptions.DeleteType.Images); deleteOptions.ForceDelete = true; //STEP 2: Call the mass delete method on the ArtifactIDs you want to delete. var result = proxy.MassDeleteDocuments(proxy.APIOptions, deleteOptions, new List<int> {1033456, 1033457, 1033458}); //STEP 3: Check for success. if (!result.Success) { Console.WriteLine(result.Message); return false; } return true; } Relativity | Services API Guide - 225 7.2.1.4 MassDelete() method The MassDelete() method removes a list of objects from the Workspace. You must specify the ArtifactType of the objects, and then list the ArtifactID of each object to delete. This code sample illustrates how to perform a cascade delete on the objects of the specified type. public static bool Using_MassDelete_ ToCascadeDeleteSpecificObjectsOfSpecificType(IRSAPIClient proxy) { //STEP 1: Set up delete options. var deleteOptions = new MassDeleteOptions(1033451); deleteOptions.CascadeDelete = true; //STEP 2: Call mass delete on the ArtifactIDs you want to delete. var result = proxy.MassDelete(proxy.APIOptions, deleteOptions, new List<int> {1033456, 1033457, 1033458}); //STEP 3: Check for success. if (!result.Success) { Console.WriteLine(result.Message); return false; } return true; } 7.2.1.5 MassDeleteAllObjects() method The MassDeleteAllObjects() method removes all objects of the specified ArtifactType from the Workspace. This code sample illustrates how to delete all objects of the specified type without using cascade delete, which means that dependent objects aren't deleted. public static bool Using_MassDelete_DeleteAllObjectsOfSpecificTypeInWorkspace (IRSAPIClient proxy) { //STEP 1: Set up delete options. var deleteOptions = new MassDeleteOptions(1033451); deleteOptions.CascadeDelete = false; //STEP 2: Call mass delete on the object type. var result = proxy.MassDeleteAllObjects(proxy.APIOptions, deleteOptions); //STEP 3: Check for success. if (!result.Success) { Console.WriteLine(result.Message); Relativity | Services API Guide - 226 return false; } return true; } 7.3 Querying With the Services API, you can perform searches using Query objects and the Query() method on the RSAPIClient class. The Query() method takes a Query object, which has Conditions that contain information about the search that you want to execute. In addition, you can combine Conditions with an operator to create compound queries. You can find code samples that illustrate how to query on a specific DTO. For more information, see DTO reference and code samples on page 81. 7.3.1 Using Query objects You can use the Query object to describe a search that you want execute. You must indicate the ArtifactType for the search by using the ArtifactTypeID, ArtifactTypeName, or ArtifactTypeGuid. You can use the Condition property on a Query to provide an expression that describes the search criteria. A Condition specifies the name of a Field, a comparison operator (such as EqualTo or GreaterThan), and a value. The list of supported comparison operators varies by Field data type. You can use the Fields collection on the Query object to specify Fields that you want returned for each item matching the search criteria. The Query() method returns a QueryResult object with those items that match the search Condition. The QueryResult object has the following properties: n n QueryArtifacts - a collection of Artifacts that were found by the query. QueryToken - when this property has a value, it indicates that the query returned more Artifacts than number of results specified by the length parameter. See Paging on the next page. You can sort query results by specifying one or more Fields in the Sorts property on the Query object. This property is a collection of Sort objects, which takes a Field name, a sort direction (ascending/descending), and a sort precedence order. (The sort order gives a high precedence to lower numbers.) Note: A query on multi-reflected Fields returns a list of the requested data type. For example, the value of a multi-reflected integer field returns a list of integers. 7.3.2 Available Conditions for Querying You can combine Conditions with the AND or OR operator to create complex queries. The Services API provides the following classes as condition types: n n n n n n BooleanCondition CompositeCondition DateTimeCondition DecimalCondition FileCondition MultiChoiceCondition Relativity | Services API Guide - 227 n n n n n n n n n NotCondition ObjectCondition ObjectsCondition SavedSearchCondition SingleChoiceCondition TextCondition UserCondition ViewCondition WholeNumberCondition 7.3.3 System Types supported by the Query() method The Query() method provides the following functionality: n n Searches for all Workspaces in Relativity, and all Fields in a specific Workspace. It supports queries for Documents and Dynamic Objects based on specified search criteria. Searches on any Field in the Available Fields or the Selected Fields list available in the view for a system type. (The view is available through the Relativity web UI.) This table summarizes the Relativity system types that the Services API supports. System Types Application Batch Batch Sets Choice Client Group Markup Sets User Object Type Tab Folder Layout View Workspace Error Field Relativity Scripts Saved Searches Query Support Yes Yes Yes Yes Yes Yes Yes Yes Yes (Artifact Type) Yes (Only Workspace tab) Yes Yes Yes Yes Yes Yes 7.3.4 Paging In the Services API, you can perform paging on query results that include Documents or Relativity Dynamic Objects (RDOs). Paging provides the functionality used to return query results in small subsets. You can use paging in searches for Documents or RDOs by calling the Query() method and passing a length parameter, which determines the number of Artifacts returned by the initial page of results. You can set the Relativity | Services API Guide - 228 length parameter on the QuerySubset() method to return Artifacts on subsequent pages. When the length parameter is set to 0, the Services API uses the default value defined for the PDVDefaultQueryCacheSize setting on the Configuration Table. This value is set to 1000, but you can update it as necessary. The following sample code illustrates how to use paging. var qry = new DTOs.Query<DTOs.Document>(); qry.Fields = DTOs.FieldValue.AllFields; var resultsFirst100 = Client.Repositories.Document.Query(qry, 100); string queryToken = resultsFirst100.QueryToken; var resultsSecond100 = Client.Repositories.Document.QuerySubset(queryToken, 101, 100); 7.3.4.1 Paging support for system objects In a Relativity workspace, all objects created by users are RDOs, so paging is supported for these objects. It is also supported for Batch and Relativity Application, which are workspace system objects that are also RDOs. Note: You can check the Boolean value assigned to the Dynamic property on an object to determine whether it is a RDO. 7.3.5 Constraints on the Query() method The Query() method is subject to the following constraints: n n n n n Queries are limited to only a single object type. Conditions (that is search criteria) only support comparison against literal values, such as True/False, "Hello World”, or 10.5. You can't compare one Field’s value against another. The NOT Condition returns the set of all items that don't meet the negated criteria. This operator uses set-based logic. For example, you might query for Documents using a numeric relational comparison operator, such as X < 5. The query for NOT X < 5 returns documents where X is Greater Than or Equal To 5, and it also returns all documents where X is NULL (that isn't set). Search criteria are only applied against Fields of the object type being returned. For example, when you search for Documents, you can only query on Fields that are associated with the Document object type. You can't query against Fields on Dynamic Objects. The list of available comparison operators is limited and varies by Field type. This list will be expanded in future releases. 7.3.6 SavedSearchCondition You can use the SavedSearchCondition to execute a saved search so that you can review its results. To execute this query, you need the ArtifactID of the saved search stored in Relativity. You can then execute the query by using the SavedSearchCondition on the Query() method. You can't combine any other Condition types with a SavedSearchCondition. Relativity | Services API Guide - 229 7.3.6.1 SelectedFields directive You can set the SelectedFields directive when you perform a query with SavedSearchCondition or a ViewCondition. The SelectedFields directive returns only the Fields displayed on a saved search or view in Relativity. The following sample code illustrates how to use this directive. query.Fields = Field.SelectedFields; You aren't limited to the Fields defined by the saved search or view when using a SavedSearchCondition or a ViewCondition. Instead, you can specify particular Fields that you want to retrieve, or you can use the AllFields directive to retrieve all Fields for an ArtifactType, such as View, User, or Document. See Field directives for DTOs on page 73. 7.3.6.2 Querying for the ArtifactID of a Saved Search You can query for the ArtifactID of a Saved Search. After the ArtifactID is returned, you can use it to run the Saved Search. The following code sample illustrates this process. public static bool Query_For_Saved_SearchID(IRSAPIClient proxy) { // STEP 1: If you don't have the ArtifactID of a Saved Search, you can query to find it. var query = new Query(); query.ArtifactTypeID = (Int32)ArtifactType.Search; query.Condition = new TextCondition("Name", TextConditionEnum.Like, "Batch Search"); QueryResult result = null; try { result = proxy.Query(proxy.APIOptions, query); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } // STEP 2: Save the ArtifactID of the returned saved search. int searchArtifactID = result.QueryArtifacts[0].ArtifactID; return true; } 7.3.6.3 Querying for a Document with a SavedSearchCondition As illustrated in the following code sample, you can use the SavedSearchCondition to query for a Document with a saved search ID. A SavedSearchCondition can't be combined with any other Condition types. Relativity | Services API Guide - 230 using System; using kCura.Relativity.Client; using kCura.Relativity.Client.DTOs; public class A { public static void Query_Documents_By_Saved_Search_ID_Using_Repository (IRSAPIClient proxy) { //STEP 1: Create a Query to describe the search you want to run. DTOs.Query<DTOs.Document> query = new DTOs.Query<DTOs.Document>(); //STEP 2: Set Condition. For this example, Relativity contains a saved search with an ArtifactID of 1036604. query.Condition = new SavedSearchCondition(1036604); //STEP 3: Set the SelectedFields directive to retrieve the fields defined by the Saved Search. query.Fields = DTOs.FieldValue.SelectedFields; //STEP 4: Perform the query. DTOs.ResultSet<DTOs.Document> docResults = proxy.Repositories.Document.Query(query); } } 7.3.7 Specialized queries with Conditions You can use Conditions when you want to perform specialized queries on Groups, Users, and Choices, as well as to create compound queries. 7.3.7.1 Querying for Groups and Users You can query for Users by building a Condition against the Groups field that takes a list of Group ArtifactIDs. This Condition returns Users associated with those Groups. In addition, you can query for Groups by building a Condition against the Users field that takes a list of User ArtifactIDs. This Condition returns Groups associated with those Users. 7.3.7.2 Querying for Admin Choices by Choice Type You can use the Query() method to retrieve the Artifact IDs and Names of the Choices associated with a specific Choice Type. The following Choice type field values are supported when querying by Choice, but they aren't visible on the front end: n n n n n n Case Tab Default Selected File Type Document Skip Group Status Password Resource Server Status Relativity | Services API Guide - 231 n n n Resource Server Type Send new password to Skip Default Preference Note: Only text condition is supported on Choice Type field. Sample Code //STEP 1: Create a Query to describe the search you want to run. Query q = new Query(); //STEP 2: Set the ArtifactTypeName to tell Relativity the type of item you want to search for. q.ArtifactTypeID = 7; q.ArtifactTypeName = "Choice"; //STEP 3: Create a Fields list to indicate which fields you want returned. q.Fields.Add(new Field("Name")); q.Fields.Add(new Field("Artifact ID")); q.Condition = new TextCondition("Choice Type", TextConditionEnum.EqualTo, "Password"); 7.3.7.3 Querying with complex compound conditional expressions Conditions may be combined into logical expressions using a CompositeCondition object. A CompositeCondition object can be initialized with two Conditions and an Operator (using And or Or). In addition, multiple CompositeConditions can be nested to create complex expressions, resulting in binary tree expressions. Since the structure of the tree drives the precedence of operator evaluation, it may significantly influence the outcome of the query. The following illustration shows how expression C1 AND C2 OR C3 may be evaluated. Relativity | Services API Guide - 232 In the first example, the AND composite condition is the root of the Condition tree, while the OR condition between C2 and C3 is given precedence. In the second example, the AND condition is given precedence and is evaluated before the OR condition at the root of the Condition tree. The following code sample illustrates how to use a CompositeCondition in a query. //Create a TextCondition to specify the search criteria. TextCondition criteria1 = new TextCondition(); criteria1.Field = "Control Number"; criteria1.Operator = TextConditionEnum.In; TextCondition criteria2 = new TextCondition(); criteria2.Field = "Group Identifier"; criteria2.Operator = TextConditionEnum.EqualTo; criteria2.Value = "999"; CompositeCondition composite = new CompositeCondition(); composite.Operator = CompositeConditionEnum.And; composite.Condition1 = criteria1; composite.Condition2 = criteria2; //Set the composite criteria as the Query's Condition. q.Condition = composite; 7.3.8 ViewCondition You can use the ViewCondition to execute a view so that you can review its contents. To execute this query, you need the ArtifactID of the view stored in Relativity. You can then execute the query by using the ViewCondition on the Query() method. You can't combine any other Condition types with a ViewCondition. 7.3.8.1 Supported Fields for ViewCondition The Services API supports the following Fields that you can use when querying with Views: Relativity | Services API Guide - 233 Artifact ID Available in Object Tab Created By Created On Display Field Group Definition Indentation Definition Field Fields supported for ViewCondition Indentation Method Last Modified By Last Modified On Name Object Type Order Owner Relativity Applications Render Links Security View Type Visible Visualization Type 7.3.8.2 SelectedFields directive You can set the SelectedFields directive when you perform a query with SavedSearchCondition or a ViewCondition. The SelectedFields directive returns only the Fields displayed on a saved search or view in Relativity. The following sample code illustrates how to use this directive. query.Fields = Field.SelectedFields; You aren't limited to the Fields defined by the saved search or view when using a SavedSearchCondition or a ViewCondition. Instead, you can specify particular Fields that you want to retrieve, or you can use the AllFields directive to retrieve all Fields for an ArtifactType, such as View, User, or Document. See Field directives for DTOs on page 73. 7.3.8.3 Querying for the ArtifactID of a View You can query for the ArtifactID of a View and then set a the ViewCondition with it. When the query executes, it retrieves Fields from the View as illustrated in the following sample code. public static bool Query_And_Execute_By_ViewID(IRSAPIClient proxy) { //STEP 1: If you don't have the ArtifactID of a View, you can query to find it. var query = new DTOs.Query<View>(); query.Condition = new TextCondition(ViewFieldNames.Name, TextConditionEnum.EqualTo, "All Fields"); DTOs.QueryResultSet<View> result = null; try { result = proxy.Repositories.View.Query(query); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } Relativity | Services API Guide - 234 //Save the ArtifactID of the returned view. var viewID = result.Results[0].Artifact.ArtifactID; //STEP 2: Create a new Query to execute the View. var executeViewQuery = new Query<DTOs.Field>(); //STEP 3: Set a ViewCondition used to pass the ArtifactID of the View. executeViewQuery.Condition = new ViewCondition(viewID); executeViewQuery.Fields = FieldValue.AllFields; //STEP 4: Call the Query method on the Field Repository. DTOs.QueryResultSet<DTOs.Field> executeViewResult = null; try { executeViewResult = proxy.Repositories.Field.Query(executeViewQuery, 5); } catch (Exception ex) { Console.WriteLine(string.Format("An error occurred: {0}", ex.Message)); return false; } //Check for success. if (executeViewResult.Success) { Console.WriteLine("Retrieved first 5 fields from View 'All Fields':"); foreach (Result<DTOs.Field> field in executeViewResult.Results) { Console.WriteLine("Field Name: {0}. Object Type: {1}. Field Type: {2}.", field.Artifact.Name, field.Artifact.ObjectType.DescriptorArtifactTypeID, field.Artifact.FieldTypeID); } } else { Console.WriteLine("Error retrieving fields."); } return true; } 7.3.8.4 Searching for Fields with a ViewCondition As illustrated in the following code sample, you can use the ViewCondition to search for Fields by their view ID. A ViewCondition can't be combined with any other Condition types. Relativity | Services API Guide - 235 using System; using kCura.Relativity.Client; using kCura.Relativity.Client.DTOs; public class A { public static void Query_Fields_By_Field_View_ID_Using_Repository (IRSAPIClient proxy) { //STEP 1: Create a Query to describe the search you want to run. DTOs.Query<DTOs.Field> query = new DTOs.Query<DTOs.Field>(); //STEP 2: Set Condition. For this example, Relativity contains a view with an ArtifactID of 1003689. query.Condition = new ViewCondition(1003689); //STEP 3: Set the SelectedFields directive to retrieve the fields defined by the view. query.Fields = DTOs.FieldValue.SelectedFields; //STEP 4: Perform the query. DTOs.ResultSet<DTOs.Field> docResults = proxy.Repositories.Field.Query (query); } } 8 Troubleshooting the Services API You can use the following information to identify the causes of Services API errors. It also provides sample code for writing errors to a log file. For more information about capturing errors, see Writing to the error log on page 112. 8.1 Common causes of Services API errors In the Services API, the following conditions result in an error: n n n Required fields aren't included in a method call Permission levels are inadequate for the specified operation Values passed to methods aren't within a predefined range or object type For many operations, a message indicating an error is returned in the ResultSet collection. Each object in the collection has a Success/Status field of type Boolean and Message description field. When an error occurs, the Success/Status field is set to False, and an error description is added to the Message field. The Services API throws an exception when a communication failure, timeout, or other serious error occurs, which is transmitted back to the client as a SOAP Fault that can be caught. Relativity | Services API Guide - 236 8.2 Error occurs when machines in a workgroup attempt to log in If you receive an error when machines that are part of a workgroup attempt to log in to the Services API, you need to synchronize your client and server clocks. (Machines on the domain are already synchronized with the server.) By default, clock synchronization has a 5 minute range. For information about synchronizing client and server clocks, see this Microsoft article (http://technet.microsoft.com/en-us/library/cc779145 (WS.10).aspx). Relativity | Services API Guide - 237 Proprietary Rights This documentation (“Documentation”) and the software to which it relates (“Software”) belongs to kCura Corporation and/or kCura’s third party software vendors. kCura grants written license agreements which contain restrictions. All parties accessing the Documentation or Software must: respect proprietary rights of kCura and third parties; comply with your organization’s license agreement, including but not limited to license restrictions on use, copying, modifications, reverse engineering, and derivative products; and refrain from any misuse or misappropriation of this Documentation or Software in whole or in part. The Software and Documentation is protected by the Copyright Act of 1976, as amended, and the Software code is protected by the Illinois Trade Secrets Act. Violations can involve substantial civil liabilities, exemplary damages, and criminal penalties, including fines and possible imprisonment. ©2013. kCura Corporation. All rights reserved. Relativity® and kCura® are registered trademarks of kCura Corporation. Relativity | Services API Guide - 238