logo

21 мая 2010 г.

BIEE: множество страниц дашборда - часть2

Первая часть

Приведенный ранее вариант решения содержит ряд подводных камней:
1. Используется механизм Direct Database Request, что потребует наличия прав на эту функциональность у всех пользователей BIEE – согласитесь, это не всегда хорошо.
2. Не применяются политики безопасности, ограничивающие доступ к странице. То есть вы видите все страницы информационной панели, даже если она для вас и не предназначена (хотя зайти на нее вы не сможете).

Исправим это.

Для начала немного модифицируем пакет, реализующий основную функциональность.

CREATE OR REPLACE PACKAGE BI_UTILS IS

FUNCTION GET_DASHBOARD_PAGES(P_USER_NAME IN VARCHAR2, P_DASHBOARD_PATH IN VARCHAR2)
RETURN bi_db_page_array
PIPELINED;

END BI_UTILS;

CREATE OR REPLACE PACKAGE BODY BI_UTILS IS

g_nqs_user varchar2(50) := 'Administrator';
g_nqs_password varchar2(50) := 'Administrator';

g_bi_wsdl_url varchar2(255) := 'http://localhost:9704/analytics/saw.dll?SoapImpl=';

g_wsdl_session_service varchar2(50) := 'nQSessionService';
g_wsdl_webcat_service varchar2(50) := 'webCatalogService';

G_RET_STS_SUCCESS constant varchar2(1) := 'S';
G_RET_STS_ERROR constant varchar2(1) := 'E';
G_RET_STS_UNEXP_ERROR constant varchar2(1) := 'U';

procedure http_post(p_url_in in varchar2,
p_data_in in varchar2,
p_data_type in varchar2 default 'text/xml',
p_action in varchar2 default null,
p_charset in varchar2 default null,
p_username_in in varchar2 default null,
p_password_in in varchar2 default null,
x_status_code out varchar2,
x_reason_phrase out varchar2,
x_response out varchar2) is
l_data_in varchar2(32767);
l_http_req utl_http.req;
l_http_resp utl_http.resp;
l_response varchar2(32767);
begin

utl_http.set_response_error_check(false);

l_http_req := utl_http.begin_request(p_url_in, 'POST');

utl_http.set_header(l_http_req, 'content-type', p_data_type);

l_data_in := convert(p_data_in, 'utf8');

utl_http.set_header(l_http_req, 'content-length', length(l_data_in));
utl_http.set_header(l_http_req, 'SOAPAction', p_action);

if p_username_in is not null then
utl_http.set_authentication(l_http_req, p_username_in, p_password_in);
end if;

utl_http.set_body_charset(l_http_req, null);
utl_http.write_text(l_http_req, l_data_in);

l_http_resp := utl_http.get_response(l_http_req);

if (l_http_resp.status_code = utl_http.HTTP_OK) then
x_status_code := G_RET_STS_SUCCESS;
else
x_status_code := G_RET_STS_ERROR;
end if;
x_reason_phrase := l_http_resp.reason_phrase;

utl_http.read_text(l_http_resp, l_response);
utl_http.end_response(l_http_resp);
x_response := l_response;

EXCEPTION
when others then
utl_http.end_response(l_http_resp);
x_status_code := G_RET_STS_UNEXP_ERROR;
x_reason_phrase := sqlerrm;
END http_post;

function get_error_response_text(p_response in varchar2) return varchar2 is
l_parser xmlparser.Parser;
l_domDoc xmldom.DOMDocument;
l_domNL xmldom.DOMNodeList;
l_domN xmldom.DOMNode;
l_ret varchar2(32767);
begin
l_parser := xmlparser.newParser;
xmlparser.parseclob(l_parser, p_response);
l_domDoc := xmlparser.getDocument(l_parser);
Xmlparser.Freeparser(l_parser);
l_domNL := xmldom.getElementsByTagName(l_domDoc, 'faultstring');
l_domN := xmldom.getFirstChild(xmldom.item(l_domNL, 0));

l_ret := xmldom.getNodeValue(l_domN);

xmldom.freeDocument(l_domDoc);

return l_ret;
end get_error_response_text;

FUNCTION GET_DASHBOARD_PAGES(P_USER_NAME IN VARCHAR2, P_DASHBOARD_PATH IN VARCHAR2)
RETURN bi_db_page_array
PIPELINED IS

l_session_id varchar2(150);

l_status_code varchar2(1);
l_reason_phrase varchar2(2000);

l_send_data varchar2(32767);
l_receive_data varchar2(32767);

l_error_explanation varchar2(2000);

l_receive_xml xmltype;

BEGIN

/*login*/
l_send_data :=
'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v5="com.siebel.analytics.web/soap/v5">' ||
' <soapenv:Header/>' ||
' <soapenv:Body>' ||
' <v5:impersonate>' ||
' <v5:name>' || g_nqs_user || '</v5:name>' ||
' <v5:password>' || g_nqs_password || '</v5:password>' ||
' <v5:impersonateID>' || p_user_name ||'</v5:impersonateID>' ||
' </v5:impersonate>' ||
' </soapenv:Body>' ||
'</soapenv:Envelope>';


http_post(p_url_in => g_bi_wsdl_url || g_wsdl_session_service,
p_data_in => l_send_data,
p_action => 'impersonateRequest',
x_status_code => l_status_code,
x_reason_phrase => l_reason_phrase,
x_response => l_receive_data);

if (l_status_code = G_RET_STS_SUCCESS) then

l_receive_xml := XMLTYPE(l_receive_data);

SELECT EXTRACT(VALUE(xmlt),
'sessionID/text()',
'xmlns="com.siebel.analytics.web/soap/v5"') .getStringVal() AS session_id
into l_session_id
FROM TABLE(XMLSEQUENCE(EXTRACT(l_receive_xml,
'//sawsoap:sessionID[1]',
'xmlns:sawsoap="com.siebel.analytics.web/soap/v5"'))) xmlt;
else

begin
l_error_explanation := get_error_response_text(p_response => l_receive_data);
exception
when others then
l_error_explanation := l_reason_phrase ||
' (cannot parse BI response)';
return;
end;
--may be put it to log-table?

end if;

/*get pages info*/
l_send_data :=
'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v5="com.siebel.analytics.web/soap/v5">' ||
' <soapenv:Header/>' ||
' <soapenv:Body>' ||
' <v5:getSubItems>' ||
' <v5:path>' || P_DASHBOARD_PATH || '</v5:path>' ||
' <v5:mask>*</v5:mask>' ||
' <v5:resolveLinks>true</v5:resolveLinks>' ||
' <v5:options>' ||
' <v5:includeACL>false</v5:includeACL>' ||
' <v5:withPermission>1</v5:withPermission>' ||
' <v5:withPermissionMask>1</v5:withPermissionMask>' ||
' <v5:withAttributes>0</v5:withAttributes>' ||
' <v5:withAttributesMask>0</v5:withAttributesMask>' ||
' </v5:options>' ||
' <v5:sessionID>' || l_session_id || '</v5:sessionID>' ||
' </v5:getSubItems>' ||
' </soapenv:Body>' ||
'</soapenv:Envelope>';

http_post(p_url_in => g_bi_wsdl_url || g_wsdl_webcat_service,
p_data_in => l_send_data,
p_action => 'getSubItemsRequest',
x_status_code => l_status_code,
x_reason_phrase => l_reason_phrase,
x_response => l_receive_data);

if (l_status_code = G_RET_STS_SUCCESS) then

l_receive_xml := XMLTYPE(l_receive_data);

for cur in (SELECT EXTRACT(VALUE(xmlt),
'itemInfo/path/text()',
'xmlns="com.siebel.analytics.web/soap/v5"') .getStringVal() AS path,
EXTRACT(VALUE(xmlt),
'itemInfo/caption/text()',
'xmlns="com.siebel.analytics.web/soap/v5"') .getStringVal() AS caption,
EXTRACT(VALUE(xmlt),
'itemInfo/signature/text()',
'xmlns="com.siebel.analytics.web/soap/v5"') .getStringVal() AS signature

FROM TABLE(XMLSEQUENCE(EXTRACT(l_receive_xml,
'//sawsoap:itemInfo',
'xmlns:sawsoap="com.siebel.analytics.web/soap/v5"'))) xmlt) loop

if (cur.signature = 'dashboardpageitem1') then
PIPE ROW(bi_db_page(cur.caption,rtrim(rtrim(cur.path, cur.caption),'/')));
end if;

end loop;

else

begin
l_error_explanation := get_error_response_text(p_response => l_receive_data);
exception
when others then
l_error_explanation := l_reason_phrase ||
' (cannot parse BI response)';
return;
end;
--may be put it to log-table?

end if;

/*logoff*/
l_send_data :=
'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v5="com.siebel.analytics.web/soap/v5">'||
' <soapenv:Header/>'||
' <soapenv:Body>'||
' <v5:logoff>'||
' <v5:sessionID>' || l_session_id || '</v5:sessionID>'||
' </v5:logoff>'||
' </soapenv:Body>'||
'</soapenv:Envelope>';


http_post(p_url_in => g_bi_wsdl_url || g_wsdl_session_service,
p_data_in => l_send_data,
p_action => 'logoffRequest',
x_status_code => l_status_code,
x_reason_phrase => l_reason_phrase,
x_response => l_receive_data);



return;
END GET_DASHBOARD_PAGES;

END BI_UTILS;


Что изменилось: в основную функцию пакета добавлен параметр P_USER_NAME , содержащий логин пользователя, с настройками безопасности которого хотим получить список страниц панели.
Теперь мы не просто соединяемся с BI под Администратором, но также делаем имперсонацию в переданного пользователя.


Чтобы избежать использования Direct database request – создадим в репозитории BI две новые физические таблицы.
Первая – DUAL (если по каким-то причинам вы ее еще не используете).
Вторая – BI_UTILS_DB_PAGES - таблица с типом Select и текстом

select page, portalpath
from table(bi_utils.get_dashboard_pages('VALUEOF(NQ_SESSION.USER)', 'VALUEOF(NQ_SESSION.PORTAL_PATH)'))

Свяжем две эти таблицы на физическом уровне (BIEE не позволяет «протянуть» через репозиторий единичную таблицу).


На логическом слое репозитория создадим новый предметный каталог, в который поместим 2 новые таблицы. Свяжем их логической связью. И новый предметный каталог отразим в презентационном слое.



Можно определить в Variable Manager новую сессионную переменную PORTAL_PATH – ее мы используем внутри таблицы-запроса BI_UTILS_DB_PAGES. Но это не принципиально (хотя если вы перфекционист и любой warning репозитория вас коробит – обязательно создайте сессионную переменную и назначьте ей значение по умолчанию).

Теперь создадим новый BI Answer, но уже как «чистый» Answer, а не DDR.


В представлениях Answer’а нам нужно лишь Примечание (Narrative View)


Префикс остается старым:

<script language="javascript">
function getDBPagePath(sPortalPath, sPage)
{
var sCmd = sawCommandToURLImpl('Dashboard');
sCmd += "&PortalPath=" + saw.encodeURIComponent(sPortalPath);
sCmd += "&Page=" + saw.encodeURIComponent(sPage);
return sCmd
}
</script>


Тело немного меняется:

<a href="javascript:void(null);" onclick="return GuidedNav(getDBPagePath('@1','@2'),true);">@2</a>



Остается только сказать системе откуда брать значение сессионной переменной PORTAL_PATH.

На вкладке «Дополнительно» определим значение данной переменной:




P.S. Для сортировки страниц информационной панели можно использовать трюк: переименуйте какую-либо страницу – добавив пробел в начало имени, тем самым страница поднимется «вверх в списке».

1 комментарий:

  1. Интересная статья. Попытался адаптировать для себя, и возникла пара вопросов:
    1. Как быть с русскими буквами в пути ? У меня выдает:

    soap:Client
    Исключение в синтаксическом анализаторе Sax.
    Сообщение: Expected end of tag &#39;soapenv:Envelope&#39;, publicId логического объекта: , systemId логического объекта: , номер строки: 1, номер столбца: 773



    UH6MBRBC
    Исключение в синтаксическом анализаторе Sax.
    Сообщение: Expected end of tag &#39;soapenv:Envelope&#39;, publicId логического объекта: , systemId логического объекта: , номер строки: 1, номер столбца: 773





    2. Можно ли с помощью веб сервиса получить список доступных дашбоардов ?

    ОтветитьУдалить