我正在尝试对Blackbaud CRM进行API调用,以获取包含选民电子邮件地址的数据列表。我已经将SOAP请求格式化为jinja2模板,并且正在使用python和请求进行调用。这是SOAP请求的规范:
POST /1234ABC_fa123b46-12a4-4119-a334-5678e2e59d29/appfxwebservice.asmx HTTP/1.1
Host: bbisec04pro.blackbaudhosting.com
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "Blackbaud.AppFx.WebService.API.1/DataListLoad"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<DataListLoadRequest xmlns="Blackbaud.AppFx.WebService.API.1">
<DataListID>guid</DataListID>
<DataListName>string</DataListName>
<ContextRecordID>string</ContextRecordID>
<Parameters>
<Values xmlns="bb_appfx_dataforms">
<fv ID="string">
<Value />
<ValueTranslation>string</ValueTranslation>
</fv>
<fv ID="string">
<Value />
<ValueTranslation>string</ValueTranslation>
</fv>
</Values>
</Parameters>
<MaxRows>int</MaxRows>
<MaxTotalRecords>int</MaxTotalRecords>
<ViewFormID>guid</ViewFormID>
<RecordIDColumn>string</RecordIDColumn>
<IncludeMetaData>boolean</IncludeMetaData>
<SecurityContext>
<SecurityFeatureID>guid</SecurityFeatureID>
<SecurityFeatureType>None or Form or DataList or RecordOperation or BusinessProcess or Dashboard or SearchList or SimpleDataList or Task or SmartQuery or AdHocQueryView or BatchType or AddCodeTableEntry or UpdateCodeTableEntry or DeleteCodeTableEntry or Batch or BatchTemplate or Kpi or MergeTask or SmartField or GlobalChange or ReportParameter or SystemPrivilege or ConfigurationData or BatchTemplateCustomize or BatchProcessor or Page or MapEntity or ExportDefinition</SecurityFeatureType>
<RecordContext>
<RecordID>string</RecordID>
<RecordType>string</RecordType>
</RecordContext>
<AttributeContext>
<AttributeCategoryID>string</AttributeCategoryID>
</AttributeContext>
</SecurityContext>
<IgnoreInvalidFilters>boolean</IgnoreInvalidFilters>
<ResultsAsXml>boolean</ResultsAsXml>
<UserSettingsPath>string</UserSettingsPath>
<SortFieldID>string</SortFieldID>
<SortDirection>Ascending or Descending</SortDirection>
<StartRowIndex>int</StartRowIndex>
<RecordToInclude>string</RecordToInclude>
<RowRangeKeyToRemove>string</RowRangeKeyToRemove>
<CancelID>string</CancelID>
<IgnoreExtraFields>boolean</IgnoreExtraFields>
</DataListLoadRequest>
</soap:Body>
</soap:Envelope>
这是我的jinja2模板:
{% extends 'client_app.xml' %}
{% block request %}<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<DataListLoadRequest xmlns="Blackbaud.AppFx.WebService.API.1">
{% block client_app %}{{ super() }}{% endblock %}
{% if guid %}<DataListID>{{ guid }}</DataListID>{% endif %}
{% if name %}<DataListName>{{ name }}</DataListName>{% endif %}
<ContextRecordID>{{ lookup_id }}</ContextRecordID>
{% if parameters %}<Parameters>
<Values xmlns="bb_appfx_dataforms">
{% for item in parameters %}
<fv ID="{{ item['id'] }}">
<Value />
<ValueTranslation>{{ item['translation'] }}</ValueTranslation>
</fv>
{% endfor %}
</Values>
</Parameters>{% endif %}
{% if max_rows %}<MaxRows>{{ max_rows }}</MaxRows>{% endif %}
{% if max_total %}<MaxTotalRecords>{{ max_total }}</MaxTotalRecords>{% endif %}
{% if view_id %}<ViewFormID>{{ view_id }}</ViewFormID>{% endif %}
{% if record_id_col %}<RecordIDColumn>{{ record_id_col }}</RecordIDColumn>{% endif %}
{% if has_metadata %}<IncludeMetaData>{{ has_metadata }}</IncludeMetaData>{% endif %}
{% if security_context %}<SecurityContext>
{% if security_context['guid'] %}<SecurityFeatureID>{{ security_context['guid'] }}</SecurityFeatureID>{% endif %}
{% if security_context['feature_type'] %}<SecurityFeatureType>{{ security_context['feature_type'] }}</SecurityFeatureType>{% endif %}
{% if security_context['record'] %}<RecordContext>
<RecordID>{{ security_context['record']['id'] }}</RecordID>
<RecordType>{{ security_context['record']['type'] }}</RecordType>
</RecordContext>{% endif %}
{% if attribute_context %}<AttributeContext>
<AttributeCategoryID>{{ attribute_context}}</AttributeCategoryID>
</AttributeContext>{% endif %}
</SecurityContext>{% endif %}
{% if ignore_invalids %}<IgnoreInvalidFilters>{{ ignore_invalids }}</IgnoreInvalidFilters>{% endif %}
<ResultsAsXml>{{ xml_result }}</ResultsAsXml>
{% if settings_path %}<UserSettingsPath>{{ settings_path }}</UserSettingsPath>{% endif %}
{% if sort_id %}<SortFieldID>{{ sort_id }}</SortFieldID>{% endif %}
{% if sort_direction %}<SortDirection>{{ sort_direction }}</SortDirection>{% endif %}
{% if start %}<StartRowIndex>{{ start }}</StartRowIndex>{% endif %}
{% if rec_to_include %}<RecordToInclude>{{ rec_to_include }}</RecordToInclude>{% endif %}
{% if to_remove %}<RowRangeKeyToRemove>{{ to_remove }}</RowRangeKeyToRemove>{% endif %}
{% if cancel_id %}<CancelID>{{ cancel_id }}</CancelID>{% endif %}
{% if ignore_extra %}<IgnoreExtraFields>{{ ignore_extra }}</IgnoreExtraFields>{% endif %}
</DataListLoadRequest>
</soap:Body>
</soap:Envelope>
{% endblock %}
以下是呈现模板的代码:
def render_data_list(
guid='',
name='',
lookup_id='',
parameters='',
max_rows='',
max_total='',
view_id='',
record_id_col='',
has_metadata='',
security_context='',
ignore_invalids='',
xml_result=0,
settings_path='',
sort_id='',
sort_direction='',
start='',
rec_to_include='',
to_remove='',
cancel_id='',
ignore_extra='',
):
template = env.get_template('data_list_load.xml')
return template.render(
guid=guid,
name=name,
lookup_id=lookup_id,
parameters=parameters,
max_rows=max_rows,
max_total=max_total,
view_id=view_id,
record_id_col=record_id_col,
has_metadata=has_metadata,
security_context=security_context,
ignore_invalids=ignore_invalids,
xml_result=xml_result,
settings_path=settings_path,
sort_id=sort_id,
sort_direction=sort_direction,
start=start,
rec_to_include=rec_to_include,
to_remove=to_remove,
cancel_id=cancel_id,
ignore_extra=ignore_extra,
)
实际上是进行呼叫的功能:
def get_list(guid='', name='', lookup_id=''):
endpoint = 'Blackbaud.AppFx.WebService.API.1/DataListLoad'
header = config.default_head.copy()
header.update({'SOAPAction': endpoint})
body = render_data_list(guid=guid, name=name, lookup_id=lookup_id)
res = config.session.post(
f'{config.base_url}{config.db_id}{config.api}',
data=body,
headers=header,
)
print(res.status_code, res.text)
这是带有参数的实际API调用:
if __name__ == '__main__':
print(get_list(guid=config.email_list_guid, lookup_id=12345678))
我收到一条错误消息:“无法加载数据列表。将数据类型nvarchar转换为uniqueidentifier时出错。”我尝试将字符串传递给lookup_id
,但仍然收到相同的错误。我试图从模板中删除<ContextRecordID>
标记,但是这些标记似乎是必需的。我正在使用Blackbaud CRM中的组成部分查找ID作为ContextRecordID,因为此数据列表的ContextRecordType是组成部分。我应该使用其他名称作为ContextRecordID还是查找ID的格式不正确?任何帮助,将不胜感激。这是完整的错误响应:
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Header><ResponseErrorHeader xmlns="Blackbaud.AppFx.WebService.API.1"><Name>DataListLoad</Name><ErrorCode>GeneralError</ErrorCode><ErrorText>Unable to load data list. Error converting data type nvarchar to uniqueidentifier.</ErrorText><ExceptionDetails>Blackbaud.AppFx.Server.ServiceException: Unable to load data list. Error converting data type nvarchar to uniqueidentifier. ---> System.Data.SqlClient.SqlException: Error converting data type nvarchar to uniqueidentifier.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.CompleteAsyncExecuteReader()
at System.Data.SqlClient.SqlCommand.InternalEndExecuteReader(IAsyncResult asyncResult, String endMethod)
at System.Data.SqlClient.SqlCommand.EndExecuteReaderInternal(IAsyncResult asyncResult)
at System.Data.SqlClient.SqlCommand.EndExecuteReader(IAsyncResult asyncResult)
at Blackbaud.AppFx.Server.AsyncSupport.CancellableSqlCommandHelper.EndExecuteReader(IAsyncResult asyncResult)
at Blackbaud.AppFx.Server.DataListLoadProcessor.EndProcessRequest(IAsyncResult asyncResult)
--- End of inner exception stack trace ---
at Blackbaud.AppFx.Server.DataListLoadProcessor.EndProcessRequest(IAsyncResult asyncResult)
at Blackbaud.AppFx.Server.AsyncRequestProcessor`2.EndGetReply(IAsyncResult asyncResult)
at Blackbaud.AppFx.Server.AppService.EndDispatchRequest(IAsyncResult asyncResult)</ExceptionDetails><RootRequestName>DataListLoadRequest</RootRequestName><RequestProcessorName>Blackbaud.AppFx.Server.DataListLoadProcessor</RequestProcessorName></ResponseErrorHeader></soap:Header><soap:Body><soap:Fault><faultcode>soap:Server</faultcode><faultstring>Server was unable to process request. ---> Unable to load data list. Error converting data type nvarchar to uniqueidentifier. ---> Error converting data type nvarchar to uniqueidentifier.</faultstring><detail /></soap:Fault></soap:Body></soap:Envelope>
答案 0 :(得分:0)
查阅ID不是ContextRecordID所期望的。相反,您需要该组成部分的系统记录ID,它是guid。在组成查询的输出中,通常将此值指定为QUERYRECID
。 SQL获取所有组成系统记录ID:
select distinct [V_QUERY_CONSTITUENT].[ID]
我将通话更改为:
if __name__ == '__main__':
print(get_list(guid=config.email_list_guid,
system_id='c7bb123f-1f9a-49a4-b2fc-456be05bc334'))
请注意,我将lookup_id
的所有实例都更改为system_id
。