Understanding the Search Results
70 min
as mentioned above, the return value of the search web method is a list of list of webservicebusinessobject objects (i e a two dimensional array) the previous section provided a simple search example, where the records are returned against a single business object (e g “incident”) when performing searches against the current object and related objects, it is important to note that each item in the two dimensional array represents the matching parent child combination the best way to understand the format of the search results is to consider the following four search scenarios, with the above point in mind scenario 1 perform a search against a single business object, which returns an exact match assume the search is defined against a single business object (e g incident), and the search result returns 1 exact record here, objlist\[0]\[0] contains the matching incident record consider the following code sample objectquerydefinition query = new objectquerydefinition(); fieldclass\[] fieldobjects = new fieldclass\[] { new fieldclass() { name = "incidentnumber", type = "text" }, new fieldclass() { name = "status", type = "text" } }; query select = new selectclass(); query select fields = fieldobjects; query from = new fromclass(); query from object = "incident"; query where = new ruleclass\[] { new ruleclass() { field = "incidentnumber", condition = "=", value = "10001" } }; frsheatintegrationsearchresponse searchresponse = frsvc search(authsessionkey, tenantid, query); if (searchresponse status == "success") { webservicebusinessobject\[]\[] objlist = searchresponse objlist; foreach (webservicebusinessobject\[] objouterlist in objlist) { foreach (webservicebusinessobject obj in objouterlist) { webservicefieldvalue\[] objfieldlist = obj fieldvalues; console writeline(" with \\" \\" matches the selection criteria", obj businessobjectname, objfieldlist\[0] name, objfieldlist\[0] value); } } } } here we are searching specifically for incident 10001 in the tenant assuming the record exists in the tenant, it will be available via objlist\[0]\[0] scenario 2 perform a search against a single business object, which returns several matches assume the search is defined against a single business object (e g “incident”), and the search result returns n matching records in this case, the matching records can be accessed from objlist\[0]\[0] through objlist\[n 1]\[0] for example, assume the search returns 10 incident records – the incident records can be accessed from objlist\[0]\[0] through objlist\[9]\[0] here, the first index in the two dimensional array changes, but the second index remains at 0 consider the following code sample objectquerydefinition query = new objectquerydefinition(); fieldclass\[] fieldobjects = new fieldclass\[] { new fieldclass() { name = "incidentnumber", type = "text" }, new fieldclass() { name = "status", type = "text" } }; query select = new selectclass(); query select fields = fieldobjects; query from = new fromclass(); query from object = "incident"; query where = new ruleclass\[] { new ruleclass() { field = "incidentnumber", condition = "<=", value = "10010" } }; query orderby = new orderbyclass\[] { new orderbyclass() { name = "incidentnumber", direction = "asc" } }; frsheatintegrationsearchresponse searchresponse = frsvc search(authsessionkey, tenantid, query); if (searchresponse status == "success") { webservicebusinessobject\[]\[] objlist = searchresponse objlist; foreach (webservicebusinessobject\[] objouterlist in objlist) { foreach (webservicebusinessobject obj in objouterlist) { webservicefieldvalue\[] objfieldlist = obj fieldvalues; console writeline(" with \\" \\" matches the selection criteria", obj businessobjectname, objfieldlist\[0] name, objfieldlist\[0] value); } } } } compared to the code from scenario 1, only the following has been updated query where = new ruleclass\[] { new ruleclass() { field = "incidentnumber", condition = "<=", value = "10010" } }; here, we are searching for incident records where the incidentnumber is less than or equal to 10010 assuming that the tenant has incident records starting from 10001 through 10010, incident 10001 can be accessed via objlist\[0]\[0], incident 10002 can be accessed via objlist\[1]\[0], up to incident 10010 which can be accessed via objlist\[9]\[0] the following diagram illustrates how each of the items in the collection can be accessed individually, via the two dimensional array of search results since there are no child objects to be searched against, all of the items can be accessed using objlist\[n]\[0] scenario 3 perform a search against a single business object and its related child objects, which returns one exact match for the parent object assume we are searching for incidents with matching journal email records assume that the search returns with one matching incident record, which contains four related journal email records when performing searches against the current object and related objects, it is important to note that each item in the two dimensional array represents the matching parent / child combination in this scenario, since there is only one matching parent incident record, the search results will contain the same parent record four times, one for each matching journal email record so the parent incident record can be accessed using either objlist\[0]\[0], objlist\[1]\[0], objlist\[2]\[0], or objlist\[3]\[0] here, the first index value varies, and the second index value will be 0 (to denote the main object) each individual matching journal email child record can be accessed using objlist\[0]\[1], objlist\[1]\[1], objlist\[2]\[1], and objlist\[3]\[1] here, the first index value varies (to correspond to each matching journal email record), and the second index will be 1 (to denote the first child object) consider the following code sample objectquerydefinition query = new objectquerydefinition(); fieldclass\[] fieldobjects = new fieldclass\[] { new fieldclass() { name = "incidentnumber", type = "text" }, new fieldclass() { name = "journal subject", type = "text" } }; query select = new selectclass(); query select fields = fieldobjects; query from = new fromclass(); query from object = "incident"; query from links = new fromlinkclass\[] { new fromlinkclass { relation = "", object = "journal#email" } }; query where = new ruleclass\[] { new ruleclass() { field = "incidentnumber", condition = "=", value = "10001" }, new ruleclass() { field = "journal category", condition = "=", value = "incoming email" }, }; query orderby = new orderbyclass\[] { new orderbyclass() { name = "incidentnumber", direction = "asc" }, new orderbyclass() { name = "journal subject", direction = "asc" } }; frsheatintegrationsearchresponse searchresponse = frsvc search(authsessionkey, tenantid, query); if (searchresponse status == "success") { webservicebusinessobject\[]\[] objlist = searchresponse objlist; foreach (webservicebusinessobject\[] objouterlist in objlist) { foreach (webservicebusinessobject obj in objouterlist) { webservicefieldvalue\[] objfieldlist = obj fieldvalues; console writeline(" with matches the selection criteria", obj businessobjectname, objfieldlist\[0] name, objfieldlist\[0] value); } } } here, we are searching for incident records where the incidentnumber is equal to 10001, and has associated journal email records with category of “incoming email” (i e emails attached to the incident record, via the email listener) because the search needs to consider not only the top level object (i e “incident”), but also the related “journal email” records, additional lines of code needs to be written first off, the fromlinkclass needs to be specified query from links = new fromlinkclass\[] { new fromlinkclass { relation = "", object = "journal#email" } }; here, we create a new instance of the fromlinkclass, which designates the relationship from incident to journal email the existing “incidentcontainsjournal” relationship (which points to the journal base object) can be used to associate the incident with the child journal records because the relationship has no value specified for the internal reference name parameter, the relation value is left as an empty string, in the relation member above the actual object to be searched against (in this case, journal email) needs to be specified above in the object member with the links property specified for the fromclass, the fields from the related business object can be accessed for example, besides accessing the incidentnumber field of the incident, the subject of the journal email can be accessed fieldclass\[] fieldobjects = new fieldclass\[] { new fieldclass() { name = "incidentnumber", type = "text" }, new fieldclass() { name = "journal subject", type = "text" } }; notice that to reference the subject field in the journal email business object, the field name needs to be specified as “journal subject” similarly, to specify the rule condition using fields in the journal email business object (e g “category”), the field name needs to be specified as “journal category” query where = new ruleclass\[] { new ruleclass() { field = "incidentnumber", condition = "=", value = "10001" }, new ruleclass() { field = "journal category", condition = "=", value = "incoming email" }, }; so in the example scenario, assume that incident 10001 exists in the tenant, with four journal emails attached to it, with subject values of “email 1”, “email 2”, “email 3”, and “email 4” running the above sample code yields the following results in the console window incident with incidentnumber "10001" matches the selection criteria journal email with journal subject "email 1" matches the selection criteria incident with incidentnumber "10001" matches the selection criteria journal email with journal subject "email 2" matches the selection criteria incident with incidentnumber "10001" matches the selection criteria journal email with journal subject "email 3" matches the selection criteria incident with incidentnumber "10001" matches the selection criteria journal email with journal subject "email 4" matches the selection criteria as mentioned in the previous sections, the results are returned for each parent / child combination because the four journal email records are associated with the same incident record (incident 10001), this same incident record will show up four times, once for each journal email child record associated with it the following diagram illustrates how each of the items in the parent / child combination can be accessed individually, via the two dimensional array of search results scenario 4 perform a search against a single business object and its related child objects, which returns several matches for the parent object assume we are searching for incidents with matching journal email records assume that the search returns with two matching incident records, which contains six related journal email records – four for the first incident record (e g incident 10001), and two for the second incident record (e g incident 10008) as before, recall that when performing searches against the current object and related objects, it is important to note that each item in the two dimensional array represents the matching parent / child combination in this scenario, since there are two matching parent incident records, and six related journal email records, the search results will return six results the first incident record will show up using either objlist\[0]\[0], objlist\[1]\[0], objlist\[2]\[0], and objlist\[3]\[0], and its corresponding journal email records will show up via objlist\[0]\[1], objlist\[1]\[1], objlist\[2]\[1], and objlist\[3]\[1] the second incident record will show up using either objlist\[4]\[0] and objlist\[5]\[0], and its corresponding journal email records will show up via objlist\[4]\[1] and objlist\[5]\[1] consider the following code sample objectquerydefinition query = new objectquerydefinition(); fieldclass\[] fieldobjects = new fieldclass\[] { new fieldclass() { name = "incidentnumber", type = "text" }, new fieldclass() { name = "journal subject", type = "text" } }; query select = new selectclass(); query select fields = fieldobjects; query from = new fromclass(); query from object = "incident"; query from links = new fromlinkclass\[] { new fromlinkclass { relation = "", object = "journal#email" } }; query where = new ruleclass\[] { new ruleclass() { field = "incidentnumber", condition = "<=", value = "10010" }, new ruleclass() { field = "journal category", condition = "=", value = "incoming email" }, }; query orderby = new orderbyclass\[] { new orderbyclass() { name = "incidentnumber", direction = "asc" }, new orderbyclass() { name = "journal subject", direction = "asc" } }; frsheatintegrationsearchresponse searchresponse = frsvc search(authsessionkey, tenantid, query); if (searchresponse status == "success") { webservicebusinessobject\[]\[] objlist = searchresponse objlist; foreach (webservicebusinessobject\[] objouterlist in objlist) { foreach (webservicebusinessobject obj in objouterlist) { webservicefieldvalue\[] objfieldlist = obj fieldvalues; console writeline(" with matches the selection criteria", obj businessobjectname, objfieldlist\[0] name, objfieldlist\[0] value); } } } compared to the code from scenario 3, only the following has been updated query where = new ruleclass\[] { new ruleclass() { field = "incidentnumber", condition = "<=", value = "10010" } }; here, we are searching for incident records where the incidentnumber is less than or equal to 10010, and has associated journal email records with category of “incoming email” (i e emails attached to the incident record, via the email listener) so in the example scenario, assume that incident 10001 exists in the tenant, with four journal emails attached to it, with subject values of “email 1”, “email 2”, “email 3”, and “email 4” assume that incident 10008 also exists in the tenant, with two journal email records attached to it, with subject values of “email 5” and “email 6” running the above sample code yields the following results in the console window incident with incidentnumber "10001" matches the selection criteria journal email with journal subject "email 1" matches the selection criteria incident with incidentnumber "10001" matches the selection criteria journal email with journal subject "email 2" matches the selection criteria incident with incidentnumber "10001" matches the selection criteria journal email with journal subject "email 3" matches the selection criteria incident with incidentnumber "10001" matches the selection criteria journal email with journal subject "email 4" matches the selection criteria incident with incidentnumber "10008" matches the selection criteria journal email with journal subject "email 5" matches the selection criteria incident with incidentnumber "10008" matches the selection criteria journal email with journal subject "email 6" matches the selection criteria as mentioned in the previous sections, the results are returned for each parent / child combination because there are four journal email records associated with incident 10001, this same incident record will show up four times, once for each of the four journal email child records associated with it afterwards, there are two journal email records associated with incident 10008, so this same incident will show up two times, once for each of the two journal email child records associated with it in total, there are six such parent / child combinations, so there are twelve object records returned (six for the incident, and six for the distinct journal email records) the following diagram illustrates how each of the items in the parent / child combination can be accessed individually, via the two dimensional array of search results grouping the rule criteria the previous section describes how to search for records based on the current business object (e g “incident”) and its related child business objects (e g “journal email”) the earlier examples describe how to formulate queries such as the following retrieve all incident records with incidentnumber less than 10010, containing related journal email records with category of “incoming email” the search webmethod is actually flexible enough to express searches through the grouping of the rule criteria for example, the search webmethod allows one to express queries such as the following retrieve all incident records with incidentnumber less than 10010, containing related journal email records where (the category of the journal email is “incoming email” or the subject of the journal email is “urgent request”) the above search allows the user to search for incidents containing journal email records, either if the journal email has a category of “incoming email” (i e it was created via the email listener), or the subject of the journal email has a subject line of “urgent request” regardless if the email has a category of “incoming email” or “outgoing email” to express the above search, consider the following code sample objectquerydefinition query = new objectquerydefinition(); fieldclass\[] fieldobjects = new fieldclass\[] { new fieldclass() { name = "incidentnumber", type = "text" }, new fieldclass() { name = "journal category", type = "text" } }; query select = new selectclass(); query select fields = fieldobjects; query from = new fromclass(); query from object = "incident"; query from links = new fromlinkclass\[] { new fromlinkclass { relation = "", object = "journal#email" } }; query where = new ruleclass\[] { new ruleclass { join = "and", field = "incidentnumber", condition = "<=", value = "10010" }, new ruleclass { rules = new ruleclass\[] { new ruleclass { field = "journal category", condition = "=", value = "outgoing email" }, new ruleclass { join = "or", field = "journal subject", condition = "=", value = "urgent request" } } } }; frsheatintegrationsearchresponse searchresponse = frsvc search(authsessionkey, tenantid, query); if (searchresponse status == "success") { webservicebusinessobject\[]\[] objlist = searchresponse objlist; foreach (webservicebusinessobject\[] objouterlist in objlist) { foreach (webservicebusinessobject obj in objouterlist) { webservicefieldvalue\[] objfieldlist = obj fieldvalues; console writeline(" with \\" \\" matches the selection criteria", obj businessobjectname, objfieldlist\[0] name, objfieldlist\[0] value); } } } this code sample is a variation of the samples from the earlier section, with several important differences in particular, notice the update to the where property for the objectquerydefinition object query where = new ruleclass\[] { new ruleclass { join = "and", field = "incidentnumber", condition = "<=", value = "10010" }, new ruleclass { rules = new ruleclass\[] { new ruleclass { field = "journal category", condition = "=", value = "outgoing email" }, new ruleclass { join = "or", field = "journal subject", condition = "=", value = "urgent request" } } } }; at the top level are two ruleclass objects – one for expression the condition to search for incident records with incidentnumber less than or equal to 10010, as illustrated in the previous sections new ruleclass { join = "and", field = "incidentnumber", condition = "<=", value = "10010" }, the second ruleclass is used solely to populate the rules member variable of the ruleclass new ruleclass { rules = new ruleclass\[] { } } so inside the second ruleclass, another ruleclass array is being instantiated with two inner ruleclasses new ruleclass { field = "journal category", condition = "=", value = "outgoing email" }, new ruleclass { join = "or", field = "journal subject", condition = "=", value = "urgent request" } so the inner ruleclass array essentially allows one to express the following portion of the search retrieve the related journal email records where either (the category of the journal email is “incoming email” or the subject of the journal email is “urgent request”) to express the following related query retrieve the related journal email records where either (the category of the journal email is “incoming email” and the subject of the journal email is “urgent request”) the join member variable needs to be changed from “or” to “and”, as follows new ruleclass { field = "journal category", condition = "=", value = "outgoing email" }, new ruleclass { join = "and", field = "journal subject", condition = "=", value = "urgent request" } with the above example, it can be seen how rules can be grouped together, by populating the rules member variable of the ruleclass object that is, the ruleclass uses composition to allow ruleclasses to be arbitrarily grouped together, using and or or operators via the join property full text searching besides regular sql style searches, the search webmethod also supports performing full text searches against a business object (e g search for records containing the terms “email down”, against the full text catalog of the incident object) the ruleclass contains a member called conditiontype, which is of type searchconditiontype an enumeration with two permissible values byfield 0 (regular sql search) bytext 1 (full text sql search) public enum searchconditiontype { byfield = 0, bytext = 1 } regular searches are performed by setting searchconditiontype to byfield – this is the default mode for searching, so it is not necessary to have this explicitly set during the ruleclass instantiation to perform full text searches, it is necessary to explicitly set the conditiontype of the ruleclass to bytext new ruleclass() { join = "and", condition = "=", conditiontype = searchconditiontype bytext, value = "email down" } notice in particular, that the field member (which was present in the earlier search examples) is not specified in the ruleclass – since the search is now performed against the full text catalog (by virtue of the conditiontype value of bytext), it is an error to also specify field member in the ruleclass the following code sample illustrates how to search for incident records with the matching terms “email down” objectquerydefinition query = new objectquerydefinition(); query select = new selectclass(); // retrieve just the incidentnumber field value from the incident, // when invoking the search fieldclass\[] incidentfieldobjects = new fieldclass\[] { new fieldclass() { name = "incidentnumber", type = "text" }, new fieldclass() { name = "service", type = "text" } }; query select fields = incidentfieldobjects; query from = new fromclass(); query from object = "incident"; query where = new ruleclass\[] { new ruleclass() { join = "and", condition = "=", conditiontype = searchconditiontype bytext, value = "email down" }, }; frsheatintegrationsearchresponse searchresponse = frsvc search(authsessionkey, tenantid, query); if (searchresponse status == "success") { webservicebusinessobject\[]\[] incidentlist = searchresponse objlist; foreach (webservicebusinessobject\[] incidentouterlist in incidentlist) { foreach (webservicebusinessobject incident in incidentouterlist) { // since we are just retrieving one field in the selection criteria // (i e incidentnumber), this corresponds to // incident fieldvalues\[0] value when retrieving the results console writeline("incident matches the selection criteria", incident fieldvalues\[0] value); } } } formal description of the classes used by the search webmethod from the previous sections, it can be seen that in order to execute the search webmethod properly, objects from several classes need to created and populated accordingly, before the web method is called now that the earlier sections have presented several example use cases, this section will formally describe the various classes in greater detail objectquerydefinition the objectquerydefinition class is defined as follows class objectquerydefinition { int top; bool distinct; fromclass from; selectclass select; list\<ruleclass> where; list\<orderbyclass> orderby; } from the class definition, it can be seen that the objectquerydefinition is used to model the various portions of a typical sql select statement, specifically from clause class fromclass select clause class selectclass where clause class ruleclass (implemented as a list) order by clause class orderbyclass (implemented as a list) with the exception of ruleclass (used to model the where clause in the sql select statement), the other classes are named according to the corresponding clause in the sql select statement besides these classes, notice that there is also an integer member variable called “top” – this can be used to constrain the number of records being returned by the search (e g return the first 1000 matching results) there is also a boolean member variable called “distinct” – this can be used to return the distinct results, to eliminate the repeated values in the search results the following sections will describe the various component classes, which are used to model the respective clauses in the select statement fromclass the fromclass class is used to model the from clause in a typical sql select statement, and is defined as follows class fromclass { string object; list\<fromlinkclass> links; } the object member variable needs to be populated with the name of the business object to search against query from = new fromclass(); query from object = "incident"; if the saved search needs to be performed relative to specific child objects, the links member variable also needs to be populated, using a list of fromlinkclass objects class fromlinkclass { string relation; string object; } the fromlinkclass contains two member variables the “relation” member variable specifies the name of the internal reference name of the relationship between the parent and child object for example, for the “incidentcontainsjournal” relationship, the internal reference name of the relationship is blank – so to use this relationship, populate the object member variable with the name of the child business object, and leave the relation member variable as an empty string the “object” member variable specifies the name of the child business object (e g “journal#email”) from the earlier code samples, recall that to search for the journal email records related to the current business object, the links member variable of the fromclass needs to be populated as follows query from links = new fromlinkclass\[] { new fromlinkclass { relation = "", object = "journal#email" } }; selectclass the selectclass class is used to model the select clause in a typical sql select statement, and is defined as follows class selectclass { bool all; list\<fieldclass> fields; } to select all the fields in the business object, create a new selectclass object, and set the “all” member variable of the object to true query select = new selectclass(); query select all = true; note that if the search is against the main business object (e g “incident”) and its related child business objects (e g “journal email”), setting the “all” member variable to true will return all the fields in the main business object, and all the fields in the child business object to restrict the set of fields to be returned by the search web method, create a new list of fieldclass objects, and initialize it with fieldclass objects, corresponding to the fields of interest from the earlier code samples, recall that to return the incidentnumber field of the incident business object, and the category field of the child journal email object, the following statements are used fieldclass\[] fieldobjects = new fieldclass\[] { new fieldclass() { name = "incidentnumber", type = "text" }, new fieldclass() { name = "journal category", type = "text" } }; query select = new selectclass(); query select fields = fieldobjects; in particular, for fields from the main business object, the names of the fields can be provided as is, whereas for fields in the child business objects, the name of the field needs to be prefixed with the name of the child business object in the relationship so for example, if the “incidentcontainsjournal” relationship is used, the relationship is defined against the incident and journal (base) object, the category field should be specified as “journal category” ruleclass the ruleclass class is used to model the where clause in a typical sql select statement, and is defined as follows class ruleclass { string join; string condition; searchconditiontype conditiontype; string field; string value; list\<ruleclass> rules; } the field member variable is used to designate the name of the field, and the value member variable specifies the value corresponding to the field in particular, for fields from the main business object, the names of the fields can be provided as is, whereas for fields in the child business objects, the name of the field needs to be prefixed with the name of the child business object in the relationship so for example, if the “incidentcontainsjournal” relationship is used, the relationship is defined against the incident and journal (base) object, the category field should be specified as “journal category” for example, the following statement can be used to search for incident records with a priority value equal to 1, where the category value of the related journal email records is equal to “incoming email” query where = new ruleclass\[] { new ruleclass() { join = "and", condition = "=", field = "priority", value = "1" }, new ruleclass() { join = "and", condition = "=", field = "journal category", value = "incoming email" } }; the join property can contain a value of either “and” / “or”, for specifying how the ruleclass objects are to be related to one another the condition member variable specifies the comparison operator used for relating the field and the specified value the allowable values for the condition member variable include the following operator meaning = equal to != not equal to > greater than < less than >= greater than or equal <= less than or equal for grouping the rule criteria together, notice that within the ruleclass class, there is a member variable called rules, which can optionally hold a list of ruleclass objects the rules member variable can therefore be used to group related ruleclass objects together, by composing the ruleclass objects recall from the earlier “grouping the rule criteria” section, the following code example query where = new ruleclass\[] { new ruleclass { join = "and", field = "incidentnumber", condition = "<=", value = "10010" }, new ruleclass { rules = new ruleclass\[] { new ruleclass { field = "journal category", condition = "=", value = "outgoing email" }, new ruleclass { join = "or", field = "journal subject", condition = "=", value = "urgent request" } } } }; from the above example, it can be seen that inside the second ruleclass at the top level, the rules member variable is being initialized with another, inner list of ruleclass objects, where the criteria for journal email is being expressed as explained in the earlier “full text searching” section, normal sql style searches are performed, where the conditiontype is set to the enumeration value of searchconditiontype byfield – this is the default mode for searches, and does not need to be explicitly specified in the ruleclass instantiation to support full text searches, set the conditiontype to the enumeration value of searchconditiontype bytext, and do not include the field member, when instantiating the ruleclass orderbyclass the orderbyclass class is used to model the order by clause in a typical sql select statement, and is defined as follows class orderbyclass { public string name; public string direction; } here, the name member variable is used to specify the field to be ordered against, and the direction member variable specifies whether the records should be specified in ascending or descending order, using the values of “asc” or “desc”, respectively for example, from the earlier code samples, assume that the search returns the incident and related journal email records if the incident records should be sorted in ascending order based on the incidentnumber, and the related journal subject records should be sorted in ascending order based on subject, the following code can be used for this query orderby = new orderbyclass\[] { new orderbyclass() { name = "incidentnumber", direction = "asc" }, new orderbyclass() { name = "journal subject", direction = "asc" } }; record operations createobject this webmethod creates a new instance of a single business object, and may also establish relationships with other objects runs initialization rules first, then applies the supplied values to the fields and invokes auto fill, calculated, save and business rules in the same way, if the object was being created interactively via ui validation rules are also executed and they might prevent the saving operation, if the resulting object field values do not pass the validation fields are initialized in the order provided request syntax frsheatintegrationcreateboresponse createobject(string sessionkey, string tenantid, objectcommanddata commanddata) parameters sessionkey key received in the earlier connect request tenantid tenant for which the key is authenticated commanddata a structure containing information about the creation request public class objectcommanddata { public string objectid; public string objecttype; public list\<objectcommanddatafieldvalue> fields; public list\<linkentry> linktoexistent; } objectid recid of the object to be created objecttype type of the object to be created fieldvalues a list of name value pairs, containing the field names and new values for the fields of the business object that should be populated linktoexistent references the linkentry class, which controls whether relationships between this object and other objects should be established public class linkentry { public string action; public string relation; public string relatedobjecttype; public string relationshipname; public string relatedobjectid; public list\<searchcondition> searchcriteria; } the following are the field members of the linkentry class action either “link” or “unlink” – determines whether this object is to be linked with the other objects or unlinked only “link” is meaningful in createobject operation relation the relationship tag (shown as internal reference name in admin ui) for the relationship type to be established relationshipname the relationship name (shown as display name in admin ui) for the relationship type to be established relatedobjecttype the type of the business object in an object reference notation to be linked with relatedobjectid the recid of the business object to be linked or unlinked optional, either relatedobjectid or searchcriteria must be provided searchcriteria a list of structures defining search criteria for matching objects that have to be linked with this object public class searchcondition { public string objectid; public string objectdisplay; public string joinrule; public string condition; public searchconditiontype conditiontype; public string fieldname; public string fielddisplay; public string fieldalias; public string fieldtype; public string fieldvalue; public string fieldvaluedisplay; public string fieldvaluebehavior; public string fieldstartvalue; public string fieldendvalue; public list\<searchcondition> subquery; } objectid recid of the object to match objectdisplay no definition present joinrule determines how individual search criteria combine together possible values are “and” and “or” condition how the field value should be matched possible values = equal to != not equal to > greater than < less than >= greater or equal <= less or equal > begin with {} contains !{} does not contain 0 is empty !0 is not empty () in list !() not in list >< in range conditiontype controls how text fields are searched possible values 0 – use regular sql field search (sql like, contains clauses) 1 – use fulltext sql field search fieldname field name fieldvalue field value fieldvaluebehavior either “single” or “list” fieldstartvalue start value for “in range” condition only fieldendvalue end value for “in range” condition only return value an frsheatintegrationcreateboresponse object, defined as follows public class frsheatintegrationcreateboresponse { public string status { get; set; } public string exceptionreason { get; set; } public string recid { get; set; } public webservicebusinessobject obj { get; set; } } the frsheatintegrationcreateboresponse class comprises the following fields status – this field provides a status value indicating whether the operation was successful a full description of the available status values is provided in the table below exceptionreason – if there is an exception thrown in the course of running the webmethod, the exception information will be captured in this field recid – the recid of the newly created record, assuming the status of the web method is “success” obj – assuming the business object record can be created successfully, this field returns the record as a webservicebusinessobject object the following table lists the available status values, and describes how to interpret them status explanation success the business object can be successfully created the recid of the newly created record can be accessed via the recid field of the response object, and the obj field references the newly created webservicebusinessobject error the business object cannot be successfully created – the recid and obj fields will be null, and the exception will be stored in the exceptionreason field one typical error is the table not found exception, which occurs when the specified business object does not exist in the tenant double check to make sure that the name of the business object is spelled properly (e g “incident”, “profile employee”, etc ) the other common error encountered, is when the specified field does not exist for the business object – here, the error message would be of the form objecttablemap field \<fieldname> is not found in table \<business object># double check to make sure that the field name is spelle d correctly, and is actually defined for the given business object a third common error is to specify a value for a field, which does not exist in the associated validation list – in such cases, the following exception would be encountered \<businessobject> \<field> `\<fieldvalue>` is not in the validation list to specify date/time values, the string value should be specified using iso 8601 format, and the value itself should be relative to utc so the date/time value can be specified in one of the following two ways yyyy mm dd hh\ mm or yyyy mm ddthh\ mm either a space character or “t” character can be used to separate between the date and time values the following are two examples of specifying a date/time value of march 26th, 2013, 18 38 utc, relative to the above two formats 2013 03 26 18 38 2013 03 26t18 38 example the following example will first create a brand new change record with specific field values, then locate an existing ci computer record, by means of the search() webmethod, and will link the two records together, by means of the createobject() webmethod objectcommanddata data = new objectcommanddata(); data objecttype = "change#"; list\<objectcommanddatafieldvalue> datafields = new list\<objectcommanddatafieldvalue>(); dictionary\<string, object> fields = new dictionary\<string, object>(); fields\["requestorlink"] = "fb884d18f7b746a0992880f2dffe749c"; fields\["subject"] = "need to swap out the hard disk"; fields\["description"] = "the hard drive just crashed need to replace with a new drive from the vendor"; fields\["status"] = "logged"; fields\["typeofchange"] = "major"; fields\["ownerteam"] = "operations"; fields\["owner"] = "admin"; fields\["impact"] = "medium"; fields\["urgency"] = "medium"; fields\["cabvoteexpirationdatetime"] = "2013 03 26 18 38 30 "; foreach (string key in fields keys) { datafields add(new objectcommanddatafieldvalue() { name = key, value = fields\[key] tostring() }); } data fields = datafields toarray(); // here we will identify a ci computer record, to link to the // new change record // for this example, we will attempt to locate the ci computer record // with the name of "apac depot serv01", and retrieve its recid objectquerydefinition ciquery = new objectquerydefinition(); // just retrieve only the recid field for the ci computer record fieldclass\[] cifieldobjects = new fieldclass\[] { new fieldclass() { name = "recid", type = "text" } }; ciquery select = new selectclass(); ciquery select fields = cifieldobjects; ciquery from = new fromclass(); // search for the record against the ci computer member object ciquery from object = "ci computer"; ciquery where = new ruleclass\[] { // provide the criteria to search for the ci computer // here, we will search for the ci computer by its name new ruleclass() { condition = "=", field = "name", value = "apac depot serv01" } }; // pass in the objectquerydefinition for the query frsheatintegrationsearchresponse searchresponse = frsvc search(authsessionkey, tenantid, ciquery); webservicebusinessobject\[]\[] cilist = searchresponse objlist; // assuming that the ci record is uniquely identified by its name, and // because the above query does not join with other tables, we should be // able to locate the ci record, by accessing cilist\[0]\[0], in the // list of list of webservicebusinessobjects webservicebusinessobject ci = cilist\[0]\[0]; string cirecid = ci recid; // define the linkentry record, to link the new change record to the ci // record, by means of the recid of the change (i e cirecid), as // determined above data linktoexistent = new linkentry\[] { new linkentry() { action = "link", relation = "", relatedobjecttype = "ci#", relatedobjectid = cirecid } }; // if the record creation succeeds, the result variable will store the // recid of the new change record, otherwise it will be null frsheatintegrationcreateboresponse result = frsvc createobject(authsessionkey, tenantid, data); if (result status == "success") { console writeline("a new change record is created with recid of ", result recid); } the next example will create a new profile employee record, and link the user to the respective roles and teams notice in particular, that the password value is specified in plain text – it will be automatically converted to the internal hashed value, upon save of the record objectcommanddata data = new objectcommanddata(); data objecttype = "profile#employee"; list\<objectcommanddatafieldvalue> datafields = new list\<objectcommanddatafieldvalue>(); dictionary\<string, object> fields = new dictionary\<string, object>(); fields\["status"] = "active"; fields\["firstname"] = "brian"; fields\["lastname"] = "wilson"; fields\["loginid"] = "bwilson"; fields\["isinternalauth"] = true; // notice when setting the password for the employee, that the plain text // password is specified here it will be converted to the hashed value // upon save of the record fields\["internalauthpasswd"] = "manage1t"; fields\["primaryemail"] = " bwilson\@example com "; fields\["phone1"] = "14158665309"; // recid for the "admin" user, to serve as the manager for the new employee fields\["managerlink"] = "fb884d18f7b746a0992880f2dffe749c"; // recid for the "gmi" org unit, for the orgunit of the new employee fields\["orgunitlink"] = "4a05123d660f408997a4fee714dad111"; fields\["team"] = "it"; fields\["department"] = "operations"; fields\["title"] = "administrator"; foreach (string key in fields keys) { datafields add(new objectcommanddatafieldvalue() { name = key, value = fields\[key] tostring() }); } data fields = datafields toarray(); data linktoexistent = new linkentry\[] { // first we link the new employee to the "selfservice" and // "servicedeskanalyst" roles by recid // the internal reference name for the relationship between // profile employee and frs def role is empty, so we leave // the relation attribute in the linkentry empty in this case // link to "selfservice" role new linkentry() { action = "link", relation = "", relatedobjecttype = "frs def role#", relatedobjectid = "0a4724d8478b451abea3fb44d33db1b6" }, // link to "servicedeskanalyst" role new linkentry() { action = "link", relation = "", relatedobjecttype = "frs def role#", relatedobjectid = "06d780f5d7d34119be0d1bc8fc997947" }, // we then link the new employee to the "it" and "hr" teams // the internal reference name for the relationship between // profile employee and standarduserteam is "rev2", so we // specify this in the relation attribute in the linkentry // link to the "it" team new linkentry() { action = "link", relation = "rev2", relatedobjecttype = "standarduserteam#", relatedobjectid = "10f60157a4f34a4f9ddb140e2328c7a6" }, // link to the "hr" team new linkentry() { action = "link", relation = "rev2", relatedobjecttype = "standarduserteam#", relatedobjectid = "1ff47b9eda3049cc92458ce3249ba349" } }; frsheatintegrationcreateboresponse result = frsvc createobject(authsessionkey, tenantid, data); if (result status == "success") { console writeline("a new employee record is created with recid of ", result recid); } updateobject this webmethod updates a single object by changing its field values, and may also establish or break relationships with other objects note that the auto fill, calculated, save and business rules run during the update and they may trigger additional field changes validation rules are also executed and they might block the update operation if the resulting object field values do not pass the validation the order of operations is preserved during the update request syntax frsheatintegrationupdateboresponse updateobject(string sessionkey, string tenantid, objectcommanddata commanddata) parameters sessionkey key received in the earlier connect request tenantid tenant for which the key is authenticated commanddata see description of objectcommanddata in createobject request return value an frsheatintegrationupdateboresponse object, defined as follows public class frsheatintegrationupdateboresponse { public string status { get; set; } public string exceptionreason { get; set; } public string recid { get; set; } public webservicebusinessobject obj { get; set; } } the frsheatintegrationupdateboresponse class comprises the following fields status – this field provides a status value indicating whether the operation was successful a full description of the available status values is provided in the table below exceptionreason – if there is an exception thrown in the course of running the webmethod, the exception information will be captured in this field recid – the recid of the updated record, assuming the status of the web method is “success” obj – assuming the business object record can be updated successfully, this field returns the updated record as an webservicebusinessobject object the following table lists the available status values, and describes how to interpret them status explanation success the business object can be successfully updated the recid of the updated record can be accessed via the recid field of the response object, and the obj field references the updated webservicebusinessobject error the business object cannot be successfully updated – the recid and obj fields will be null, and the exception will be stored in the exceptionreason field one typical error is the table not found exception, which occurs when the specified business object does not exist in the tenant double check to make sure that the name of the business object is spelled properly (e g “incident”, “profile employee”, etc ) the other common error encountered, is when the specified field does not exist for the business object – here, the error message would be of the form objecttablemap field \<fieldname> is not found in table \<business object># double check to make sure that the field name is spelled correctly, and is actually defined for the given business object a third common error is to specify a value for a field, which does not exist in the associated validation list – in such cases, the following exception would be encountered \<businessobject> \<field> `\<fieldvalue>` is not in the validation list to specify date/time values, the string value should be specified using iso 8601 format, and the value itself should be relative to utc so the date/time value can be specified in one of the following two ways yyyy mm dd hh\ mm or yyyy mm ddthh\ mm either a space character or “t” character can be used to separate between the date and time values the following are two examples of specifying a date/time value of march 26th, 2013, 18 38 utc, relative to the above two formats 2013 03 26 18 38 2013 03 26t18 38 example the following example will locate an existing change record and ci computer record, by means of the search() webmethod, and will link the two records together, by means of the updateobject() webmethod // first, locate the change record to update, using the changenumber // (e g change 21) objectquerydefinition changequery = new objectquerydefinition(); // just retrieve only the recid field for the change record fieldclass\[] changefieldobjects = new fieldclass\[] { new fieldclass() { name = "recid", type = "text" } }; changequery select = new selectclass(); changequery select fields = changefieldobjects; changequery from = new fromclass(); // search for the record against the change object changequery from object = "change"; changequery where = new ruleclass\[] { new ruleclass() { // provide the criteria to search for the change // here, we will search for the change by its changenumber condition = "=", field = "changenumber", value = "21" } }; // pass in the objectquerydefinition for the query frsheatintegrationsearchresponse changesearchresponse = frsvc search(authsessionkey, tenantid, changequery); webservicebusinessobject\[]\[] changelist = changesearchresponse objlist; // assuming that the change record is uniquely identified by the // changenumber, and because the above query does not join with other // tables, we should be able to locate the change record, by accessing // changelist\[0]\[0], in the list of list of webservicebusinessobjects webservicebusinessobject change = changelist\[0]\[0]; string changerecid = change recid; // now locate the ci computer record, to link with the existing change // here we will attempt to locate the ci computer record with // the name of "apac depot serv01" and retrieve its recid objectquerydefinition ciquery = new objectquerydefinition(); // just retrieve only the recid field of the ci for the matching result fieldclass\[] cifieldobjects = new fieldclass\[] { new fieldclass() { name = "recid", type = "text" } }; ciquery select = new selectclass(); ciquery select fields = cifieldobjects; ciquery from = new fromclass(); // search for the record against the ci computer member object ciquery from object = "ci computer"; ciquery where = new ruleclass\[] { // search for the ci computer by its name new ruleclass() { condition = "=", field = "name", value = "emea exch serv01" } }; // pass in the objectquerydefinition for the query frsheatintegrationsearchresponse cisearchresponse = frsvc search(authsessionkey, tenantid, ciquery); webservicebusinessobject\[]\[] cilist = cisearchresponse objlist; // assuming that the ci record is uniquely identified by name, and // because the above query does not join with other tables, we should // be able to locate the ci record, by accessing cilist\[0]\[0], in the // list of list of webservicebusinessobjects webservicebusinessobject ci = cilist\[0]\[0]; // since we are only retrieving the recid field for ci, it will appear // as the first item in the list of fields, i e ci fieldvalues\[0] string cirecid = (string)ci fieldvalues\[0] value; // at this point, we now have the recid of the change and ci records, // and can proceed with the update // for the objectcommanddata, use the changerecid value that was // determined above, for looking up the record to update objectcommanddata data = new objectcommanddata(); data objecttype = "change#"; data objectid = changerecid; list\<objectcommanddatafieldvalue> datafields = new list\<objectcommanddatafieldvalue>(); dictionary\<string, object> fields = new dictionary\<string, object>(); // to demonstrate that the existing field value can be updated, set the // urgency of the existing change record to "high" fields\["urgency"] = "medium"; // update the cabvoteexpirationdatetime to a specific date/time value fields\["cabvoteexpirationdatetime"] = "2013 03 26 18 38 30 "; foreach (string key in fields keys) { datafields add(new objectcommanddatafieldvalue() { name = key, value = fields\[key] tostring() }); } data fields = datafields toarray(); data linktoexistent = new linkentry\[] { new linkentry() { action = "link", relation = "", relatedobjecttype = "ci#", relatedobjectid = cirecid } }; frsheatintegrationupdateboresponse response = frsvc updateobject(authsessionkey, tenantid, data); if (response exceptionreason != null) { console writeline("encountered the following error while updating the record ", response exceptionreason); } deleteobject deletes the specified business object record request syntax frsheatintegrationdeleteboresponse deleteobject(string sessionkey, string tenantid, objectcommanddata commanddata) parameters sessionkey key received in the earlier connect request tenantid tenant for which the key is authenticated commanddata see description of objectcommanddata in createobject request return value an frsheatintegrationdeleteboresponse object, defined as follows public class frsheatintegrationdeleteboresponse { public string status { get; set; } public string exceptionreason { get; set; } } the frsheatintegrationdeleteboresponse class comprises the following fields status – this field provides a status value indicating whether the operation was successful a full description of the available status values is provided in the table below exceptionreason – if there is an exception thrown in the course of running the webmethod, the exception information will be captured in this field the following table lists the available status values, and describes how to interpret them status explanation success the business object record can be successfully deleted note that a value of success is returned, either if the record is successfully deleted from the tenant, or that the indicated record cannot be found in the tenant error an error has occurred, in the process of deleting the indicated record from the system typically the error can occur if the specified business object does not exist in the system – here, the error message would be definition for business object \<businessobject># was not found in such cases, please ensure that the specified business object exists in the tenant example the following example will delete the incident record with the provided recid value objectcommanddata data = new objectcommanddata(); data objecttype = "incident#"; data objectid = "96f889a8ce6e4f9c8b3a99852f788670"; frsheatintegrationdeleteboresponse response = frsvc deleteobject(authsessionkey, tenantid, data); if (response status != "success") { console writeline("ran into the following error when deleting the record " + response exceptionreason); } attachments addattachment this webmethod adds an attachment to the specified business object record request syntax frsheatintegrationaddattachmentresponse addattachment(string sessionkey, string tenantid, objectattachmentcommanddata commanddata) parameters sessionkey key received in the earlier connect request tenantid tenant for which the key is authenticated commanddata structure containing information about the attachment public class objectattachmentcommanddata { public string objectid; public string objecttype; public string filename; public byte\[] filedata; } objectid record id of the new attachment objecttype type of the main business object to which this attachment is attached in the object reference notation filename name of the file filedata actual file bytes return value an frsheatintegrationaddattachmentresponse object, defined as follows public class frsheatintegrationaddattachmentresponse { public string status { get; set; } public string exceptionreason { get; set; } } the frsheatintegrationaddattachmentresponse class comprises the following fields status – this field provides a status value indicating whether the operation was successful a full description of the available status values is provided in the table below exceptionreason – if there is an exception thrown in the course of running the webmethod, the exception information will be captured in this field the following table lists the available status values, and describes how to interpret them status explanation success the attachment can be successfully added to the business object record error the attachment cannot be successfully added to the business object record, and the exception information is stored in the exceptionreason field in the response object one typical reason for the error, is when the attachment has a file extension which has been disallowed for the given tenant – here, the error message would be of the form the file that is uploaded is a restricted file extension please contact your system administrator for a list of allowed file extensions for upload another common reason for the error is when the business object record cannot be successfully found – here, the error message would be of the form attachment upload finished unsuccessfully a third reason for the error occurs when the specified business object does not exist in the tenant – here, the error message would be of the form definition for business object \<business object># was not found example the following example reads in a sample image from the user’s local filesystem, and attaches the file to the indicated incident record const string filename = "c \\\temp\\\sample jpg"; using (filestream fs = new filestream(filename, filemode open, fileaccess read)) { using (binaryreader r = new binaryreader(fs)) { byte\[] attachmentdata = new byte\[fs length]; for (int i = 0; i < fs length; i++) { attachmentdata\[i] = r readbyte(); } objectattachmentcommanddata data = new objectattachmentcommanddata() { objectid = "9981fbebaa8b4ee2820364505855abc2", objecttype = "incident#", filename = "sample png", filedata = attachmentdata }; frsheatintegrationaddattachmentresponse response = frsvc addattachment(authsessionkey, tenantid, data); if (response status != "success") { console writeline("encountered the following error while adding the attachment to the record " + response exceptionreason); } } } readattachment this webmethod is used for reading out the data of a specific attachment record, identified by its recid value request syntax frsheatintegrationreadattachmentresponse readattachment(string sessionkey, string tenantid, objectattachmentcommanddata commanddata) parameters sessionkey key received in the earlier connect request tenantid tenant for which the key is authenticated commanddata structure containing information about the attachment public class objectattachmentcommanddata { public string objectid; } objectid record id of the new attachment return value an frsheatintegrationreadattachmentresponse object, defined as follows public class frsheatintegrationreadattachmentresponse { public string status { get; set; } public string exceptionreason { get; set; } public byte\[] attachmentdata { get; set; } } the frsheatintegrationreadattachmentresponse class comprises the following fields status – this field provides a status value indicating whether the operation was successful a full description of the available status values is provided in the table below exceptionreason – if there is an exception thrown in the course of running the webmethod, the exception information will be captured in this field attachmentdata a byte array, containing the contents of the specified attachment the following table lists the available status values, and describes how to interpret them status explanation success the specified attachment record can be successfully retrieved from the tenant, and the data is returned via the attachmentdata byte array field in the response object notfound the specified attachment record cannot be located in the tenant double check the records in the attachment business object, to confirm that the desired attachment record does in fact exist error an unforeseen error was encountered during the retrieval of the attachment record from the tenant, and the exception information is stored in the exceptionreason field in the response object under most circumstances, the attachment retrieval should result in either a status of “success” or “notfound” example byte\[] attachmentdata; const string filename = "c \\\temp\\\test png"; objectattachmentcommanddata data = new objectattachmentcommanddata() { objectid = "94069732037142e7bf3d81db02128289", // recid of the attachment record }; frsheatintegrationreadattachmentresponse response = frsvc readattachment(authsessionkey, tenantid, data); if (response status == "success") { attachmentdata = response attachmentdata; if (attachmentdata != null) { using (filestream fs = new filestream(filename, filemode create)) { using (binarywriter w = new binarywriter(fs)) { for (int i = 0; i < attachmentdata length; i++) { w\ write(attachmentdata\[i]); } } } } } else if (response status == "notfound") { console writeline("the attachment record with the specified recid, cannot be located in the tenant"); } else { console writeline("encountered the following error while reading the attachment from the record " + response exceptionreason); } important note for net based web service clients, the application may run into the following error when retrieving large attachments, via the web service communicationexception occurred the maximum message size quota for incoming messages (65536) has been exceeded to increase the quota, use the maxreceivedmessagesize property on the appropriate binding element here, the net client application which consumes the web service, needs to be configured so that the “maxreceivedmessagesize” and “maxbuffersize” attributes are set sufficiently large in the app config file, to accommodate the large attachment sizes; e g \<binding name="ipcmservicesoap" closetimeout="00 01 00 " opentimeout="00 01 00 " receivetimeout="00🕙00" sendtimeout="00 01 00 " allowcookies="false" bypassproxyonlocal="false" hostnamecomparisonmode="strongwildcard" maxbuffersize="65536" maxbufferpoolsize="524288" maxreceivedmessagesize="65536" messageencoding="text" textencoding="utf 8" transfermode="buffered" usedefaultwebproxy="true"> \</binding> please note that the size values above are represented in bytes metadata access you can import an existing set of business objects into the frs heat tenant using the business object uploader tool the tool communicates with the frs saas application server using public web services to facilitate user logins, population of data entry forms and the upload of business object data the workflow in the business object uploader is as follows log into an instance load the list of all allowable business objects select a business object, load the metadata for the selected object complete the data for the fields being imported, if needed submit new object to system when designing your api, to know what objects you can create, you need to obtain a list of allowed objects (getallallowedobjectnames) to know what fields you want to submit to the create object call, you need to obtain the metadata (getschemaforobject) in some cases, you may want to upload objects even with “special” fields visible (like recid) (getallschemaforobject) getschemaforobject retrieves an xml version of the metadata behind a business object in the process, it screens out properties not appropriate for end user consumption, such as the recid request syntax string getschemaforobject(string sessionkey, string tenantid, string objectname) parameters sessionkey key received in the earlier connect request tenantid tenant for which the key is authenticated objectname name of the business object, in object reference notation (e g "incident#") return value a string value representing the schema for the given business object, which is represented in xml example string schemadoc = frsvc getschemaforobject(authsessionkey, tenantid, "incident#"); getallschemaforobject retrieves an xml version of the metadata behind an object, including all fields such as recid request syntax string getallschemaforobject(string sessionkey, string tenantid, string objectname) parameters sessionkey key received in the earlier connect request tenantid tenant for which the key is authenticated objectname name of the business object, in object reference notation (e g "incident#") return value a string value representing the schema for the given business object, which is represented in xml example string schemadoc = frsvc getschemaforobject(authsessionkey, tenantid, "incident#"); getallallowedobjectnames retrieves a list of all business objects request syntax list\<string> getallallowedobjectnames(string sessionkey, string tenantid); parameters sessionkey key received in the earlier connect request tenantid tenant for which the key is authenticated return value an array of strings, where each item corresponds to the name of the business object example string\[] bonamearray = frsvc getallallowedobjectnames(authsessionkey, tenantid);
