AppFxWebService and Data Forms
Note: AppFxWebService is discussed elsewhere in the guides for Blackbaud Infinity Web APIs. This topic is for data forms, particularly AppFxWebService SOAP messages for load and save request and replies.
As the generic web service for a Blackbaud Infinity application, AppFxWebService.asmx, is a loosely typed endpoint from which all data forms can be accessed. Because there are more strongly typed "wrapper" APIs for .NET clients to access AppFxWebService and a way to download a WSDL for other clients, this may not be the most common way to conceptualize API calls.
However there may be cases where access to data forms with the API doesn't look much different than the underlying SOAP messages used by the web service, especially for devices with limited resources where performance outweighs coding time. For example an Android device over a cellular network may be better served by a HttpUrlConnection and a set of mostly hard-coded SOAP request strings.
More importantly, examining these requests and responses provides insight into how Blackbaud Infinity clients interact with Blackbaud Infinity. The most important AppFxWebService.asmx web service methods for data forms are:
-
DataFormLoad
Gets the data (for edit or view) for a record defined by the supplied Form ID
-
DataFormSave
Saves (insert or update) the data for a record defined by the supplied Form ID
In the requests for these methods, you provide information about the data form, and possibly the record to access, context, and other flags. The endpoint reference provides the structure for the SOAP message for the request and what to expect as a reply.
Request - Response Structure Example
But you must also supply some additional information in the SOAP message that is not shown in the generated reference: ClientAppInfo. Here is a request recorded in the request log. The request occurred when a data form was opened from the web shell. Notice that the database and client name are provided.
Note: Requests made from the server side follow a pattern similar to web service requests.
<DataFormLoadRequest xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="Blackbaud.AppFx.WebService.API.1">
<ClientAppInfo REDatabaseToUse="BBInfinity"
ClientAppName="WebShell"
SessionKey="90ea88f0-4a63-4ee1-a781-4fed9c5bf81f"
TimeOutSeconds="120"
RunAsUserID="00000000-0000-0000-0000-000000000000" />
<FormID>c46afaa2-a2b6-4d29-ad4d-1e8cf94984bd</FormID>
<IncludeMetaData>true</IncludeMetaData>
<IncludeDataFormAddIns>true</IncludeDataFormAddIns>
<IncludeDataFormExtensions>true</IncludeDataFormExtensions>
<ExcludeValueTranslations>true</ExcludeValueTranslations>
<IncludeFieldCharacteristics>true</IncludeFieldCharacteristics>
</DataFormLoadRequest>
The reply to this request was:
<DataFormLoadReply xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="Blackbaud.AppFx.WebService.API.1"
LastUpdated="0001-01-01T00:00:00">
<FormID>c46afaa2-a2b6-4d29-ad4d-1e8cf94984bd</FormID>
<MetaData>
<FormDefinition>
<FormFields xmlns="bb_appfx_commontypes">
<FormField FieldID="SHORTNAME" MaxLength="10" Caption="Short name" DefaultValueText="" />
<FormField FieldID="LONGNAME" MaxLength="20" Caption="Long name" DefaultValueText="" />
<FormField FieldID="DESCRIPTION" Caption="Description" DefaultValueText="" />
<FormField FieldID="RATING" DataType="Decimal" Caption="Rating"
MaxValue="10" MinValue="0" Precision="18" Scale="0" DefaultValueText="0" />
</FormFields>
<WebUIComponent xmlns="bb_appfx_commontypes">
<UIModel AssemblyName="Custom.AppFx.Example.UIModel.dll"
ClassName="Custom.AppFx.Example.UIModel.ExampleAddDataFormCustomUIModel" />
<WebUI>
<ExternalResource Url="browser/htmlforms/ExampleAddDataFormCustom.html" />
</WebUI>
</WebUIComponent>
</FormDefinition>
<FormMode>Add</FormMode>
<FormHeader>Add an example</FormHeader>
<RecordType>Example (custom)</RecordType>
<CustomizedFields />
</MetaData>
<DataFormAddIns />
<DataFormExtensionLoadReplies />
</DataFormLoadReply>
Since the data form is an Add Data Form without a load implementation, no field values were returned. For a corresponding Edit Data Form, which must have a load implementation, the field values are returned by the load request.
<DataFormLoadReply xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="Blackbaud.AppFx.WebService.API.1"
LastUpdated="0001-01-01T00:00:00">
<DataFormItem>
<Values xmlns="bb_appfx_dataforms">
<fv ID="SHORTNAME">
<Value xsi:type="xsd:string">Theo</Value>
</fv>
<fv ID="LONGNAME">
<Value xsi:type="xsd:string">Theodore</Value>
</fv>
<fv ID="DESCRIPTION">
<Value xsi:type="xsd:string">Theodore is someone who is best described as...</Value>
</fv>
<fv ID="RATING">
<Value xsi:type="xsd:decimal">10.0000</Value>
</fv>
</Values>
</DataFormItem>
<TSLong>865379</TSLong>
<FormID>cd7dd1e0-a7b8-4904-8d25-9a8a5b5f4b66</FormID>
<MetaData>
<FormDefinition>
<FormFields xmlns="bb_appfx_commontypes">
<FormField FieldID="SHORTNAME" MaxLength="10" Caption="Short name" />
<FormField FieldID="LONGNAME" MaxLength="20" Caption="Long name" />
<FormField FieldID="DESCRIPTION" Caption="Description" />
<FormField FieldID="RATING" DataType="Decimal" Caption="Rating"
MaxValue="10" MinValue="0" Precision="18" Scale="0" />
</FormFields>
</FormDefinition>
<FormMode>Edit</FormMode>
<FormHeader>Edit an example</FormHeader>
<RecordType>Example</RecordType>
<CustomizedFields />
</MetaData>
<DataFormAddIns />
<DataFormExtensionLoadReplies />
</DataFormLoadReply>
When building clients to access data forms at this level, there are a few things that can help you to understand what is contained in the SOAP messages. You can log requests in the application by specifying to do so in the Web.config file located for example in C:\Program Files\Blackbaud\bbappfx\vroot.
Here is a key from a Web.config file.
<add key="logrequests" value="DataFormSaveRequest;DataListLoadRequest" />
Removing the comments surrounding this key prompts the platform to log save requests for data forms (and load requests for data lists in this case). You can add request types as necessary. The comments in the Web.config file provide some guidance. Notably this kind of logging is not appropriate for production use as it adds serialization and logging tasks for each request. The log is stored in a table called dbo.WSREQUESTLOG. You can then view the request and response messages for calls by querying dbo.WSREQUESTLOG. Since the application follows the same request -response pattern, you can use calls made by the application as examples for how to build your client's request messages. Another way to examine requests is to use a packet sniffer such as Fiddler or Wireshark.
Other data form related methods include:
-
DataFormGetDropDownValueList
Returns a list of DataFormDropDownValue rows for the given DataForm
-
DataFormInstanceGetMetaData
Returns the data form definition for a given data form name or ID
-
DataFormSupportsWebUI
Determines whether or not the specified data form instance can be rendered in a web browser
Data forms are used in conjunction with other features. For example possible values in a field may be populated by a code table or a data list feature. Those features can also be accessed through web service methods.
When a client makes a request with the DataFormLoad or DataFormSave web methods, it specifies the FormID for the data form. This is the DataFormInstanceID for the data form. The DataFormInstanceID can be found on specs for the form or from feature metadata pages for data forms accessed through Administration > Application > Features > Data form search in the application.
<FormID>guid</FormID>
In the DataFormLoadRequest example at the beginning...
<FormID>c46afaa2-a2b6-4d29-ad4d-1e8cf94984bd</FormID>
Form names are unique and can be provided as an alternative to the DataFormInstanceID.
<FormName>string</FormName>
To load Edit Data Forms and View Data Forms with DataFormLoad, the client must provide a RecordID for the record to load.
<RecordID>string</RecordID>
Here is the load request that prompted the load reply shown earlier for the Edit Data Form.
<DataFormLoadRequest xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
="Blackbaud.AppFx.WebService.API.1">
<ClientAppInfo REDatabaseToUse="BBInfinity"
ClientAppName="WebShell"
SessionKey="90ea88f0-4a63-4ee1-a781-4fed9c5bf81f"
TimeOutSeconds="120"
RunAsUserID="00000000-0000-0000-0000-000000000000" />
<FormID>cd7dd1e0-a7b8-4904-8d25-9a8a5b5f4b66</FormID>
<RecordID>660894c6-6aeb-4267-9c95-c715af8ebff1</RecordID>
<IncludeMetaData>true</IncludeMetaData>
<IncludeDataFormAddIns>true</IncludeDataFormAddIns>
<IncludeDataFormExtensions>true</IncludeDataFormExtensions>
<ExcludeValueTranslations>true</ExcludeValueTranslations>
<IncludeFieldCharacteristics>true</IncludeFieldCharacteristics>
</DataFormLoadRequest>
But notice that there was no RecordID for the load request for the Add Data Form without a load implementation. Load implementations for Add Data Forms are optional. But load requests can still be made.
To save Add Data Forms with DataFormSave, the client does not have to provide an ID for the record to add. Although the field can exist on a DataFormSave request.
<ID>string</ID>
..
<DataFormSaveRequest xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="Blackbaud.AppFx.WebService.API.1">
<ClientAppInfo REDatabaseToUse="BBInfinity"
ClientAppName="WebShell"
SessionKey="90ea88f0-4a63-4ee1-a781-4fed9c5bf81f"
TimeOutSeconds="1200"
RunAsUserID="00000000-0000-0000-0000-000000000000" />
<FormID>c46afaa2-a2b6-4d29-ad4d-1e8cf94984bd</FormID>
<FileUploadKey>12f364e7-91dd-45b3-89b0-5207db871781</FileUploadKey>
<DataFormItem>
<Values xmlns="bb_appfx_dataforms">
<fv ID="SHORTNAME">
<Value xsi:type="xsd:string">Al</Value>
</fv>
<fv ID="LONGNAME">
<Value xsi:type="xsd:string">Albert</Value>
</fv>
<fv ID="DESCRIPTION">
<Value xsi:type="xsd:string">To describe Albert...</Value>
</fv>
<fv ID="RATING">
<Value xsi:type="xsd:decimal">10</Value>
</fv>
</Values>
</DataFormItem>
<SecurityContext>
<SecurityFeatureID>c46afaa2-a2b6-4d29-ad4d-1e8cf94984bd</SecurityFeatureID>
<SecurityFeatureType>Form</SecurityFeatureType>
<RecordContext />
</SecurityContext>
</DataFormSaveRequest>
But the ID is returned in the reply.
<DataFormSaveReply xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="Blackbaud.AppFx.WebService.API.1">
<ID>F34CF0F9-1D00-416C-B4BD-247F90F2F11B</ID>
</DataFormSaveReply>
For Add Data Forms with context, when using DataFormSave, clients should specify a ContextRecordID for the parent record. For example a Constituent ID should be provided for a Constituent Address to be added. For DataFormLoad, ContextRecordID may specify a context for values to load.
<ContextRecordID>string</ContextRecordID>
Requests and responses encode field values along this pattern:
<DataFormItem>
<Values xmlns="bb_appfx_dataforms">
<fv ID="string">
<Value />
<ValueTranslation>string</ValueTranslation>
</fv>
<fv ID="string">
<Value />
<ValueTranslation>string</ValueTranslation>
</fv>
</Values>
</DataFormItem>>
A client provides values according to the DataFormItem structure in a save request to an Edit Data Form or an Add Data Form. The service would provide values to your client in load requests for each type of form using the same pattern.
The DataFormItem for the Add Data Form DataFormSaveRequest was:
<DataFormItem>
<Values xmlns="bb_appfx_dataforms">
<fv ID="SHORTNAME">
<Value xsi:type="xsd:string">Al</Value>
</fv>
<fv ID="LONGNAME">
<Value xsi:type="xsd:string">Albert</Value>
</fv>
<fv ID="DESCRIPTION">
<Value xsi:type="xsd:string">To describe Albert...</Value>
</fv>
<fv ID="RATING">
<Value xsi:type="xsd:decimal">10</Value>
</fv>
</Values>
</DataFormItem>
And the DataFormItem for an Edit Data Form DataFormLoadReply for the same record looks nearly identical with the exception of some data type formatting for the decimal field:
<DataFormItem>
<Values xmlns="bb_appfx_dataforms">
<fv ID="SHORTNAME">
<Value xsi:type="xsd:string">Al</Value>
</fv>
<fv ID="LONGNAME">
<Value xsi:type="xsd:string">Albert</Value>
</fv>
<fv ID="DESCRIPTION">
<Value xsi:type="xsd:string">To describe Albert...</Value>
</fv>
<fv ID="RATING">
<Value xsi:type="xsd:decimal">10.0000</Value>
</fv>
</Values>
</DataFormItem>
If the client indicates IncludeMetaData, the response will include a MetaData element that describes the form fields among other things. Much of this information can be gleaned from the data form's spec or the feature metadata page in the application. Returning MetaData via the web service call enables the client to programmatically build a structure with which to manipulate the form field values returned by a load request or just to build a structure to support a save request.
<IncludeMetaData>boolean</IncludeMetaData>