Сегодня хочу рассказать о том, как получить список информационных панелей Oracle BI из PL/SQL.
1) Обязательно будем учитывать, что для различных учетных записей могут быть доступны различные информационные панели.
Поэтому нам потребуется настроить имперсонацию - т.е. возможность "олицетворения" любых пользователей из-под некоторой системной учетной записи.
Нам это потребуется для того, чтобы при вызове pl/sql-функции, возвращающей список информационных панелей, не требовалось передавать пароль учетной записи, а только логин.
Итак, для начала следует в консоли EM настроить дополнительные разрешения для роли
BI Administrator
Сами разрешения:
oracle.bi.server.impersonateUser
oracle.bi.server.queryUserPopulation
Затем нам необходимо указать, что все пользовательские учетные записи позволяют себя олицетворять.
Так как, скорее всего, все интересующие нас пользователи будут входить в группу
BI Consumer, то
После произведенных манипуляций Oracle BI позволит нам войти в систему под учетной записью test, не указывая ее пароль.
http://localhost:7001/analytics/saw.dll?Dashboard&NQUser=weblogic&NQPassword=weblogic_1&Impersonate=test
2) Ну а дальше я приведу сам код PL/SQL пакета, реализующего логику получения списка информационных панелей, доступных конкретному пользователю.
Но сначала - SQL-типы, необходимые для вызова PL/SQL функции с pipelined-результатом из SQL.
create or replace type bi_dashboard as object ( dashboard_name VARCHAR2(150), dashboard_path VARCHAR2(512) ); create or replace type bi_dashboard_array as table of bi_dashboard;
CREATE OR REPLACE PACKAGE BI_UTILS IS 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'; FUNCTION GET_DASHBOARDS(P_USER_NAME IN VARCHAR2, P_SHOW_HIDDEN IN VARCHAR DEFAULT 'F') RETURN bi_dashboard_array PIPELINED; END BI_UTILS; / CREATE OR REPLACE PACKAGE BODY BI_UTILS IS G_BI_ADMIN_USER varchar2(50) := 'weblogic'; G_BI_ADMIN_PASSWORD varchar2(50) := 'weblogic_1'; G_BI_BASE_URL varchar2(512) := 'http://localhost:7001'; G_BI_LOGIN_URL varchar2(512) := G_BI_BASE_URL || '/analytics/saw.dll?Dashboard&NQUser=#ADMINUSER#&NQPassword=#ADMINPASSWORD#&Impersonate=#IMPERSONATE#'; G_BI_LOGOFF_URL varchar2(512) := G_BI_BASE_URL || '/analytics/saw.dll?Logoff'; G_BI_SEARCH_URL varchar2(512) := G_BI_BASE_URL || '/analytics/saw.dll?CatalogTreeModel'; G_ItemAttributes_NORMAL integer := 0; G_ItemAttributes_READ_ONLY integer := 1; G_ItemAttributes_ARCHIVE integer := 2; G_ItemAttributes_HIDDEN integer := 4; G_ItemAttributes_SYSTEM integer := 8; G_ItemAttributes_DO_NOT_INDEX integer := 16; /*Procedure to print strings larger than 255 chr*/ procedure output(p_string in varchar2) is l_string long default p_string; l_str_length number; l_loop_count number := 0; begin l_str_length := length(l_string); while l_loop_count < l_str_length loop dbms_output.put_line(substr(l_string, l_loop_count + 1, 255)); l_loop_count := l_loop_count + 255; end loop; end output; /*Procedure to submit HTTP requests*/ procedure http_post(p_url_in in varchar2, p_data_in in varchar2 default null, p_data_type in varchar2 default 'text/html', 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 clob) is l_data_in varchar2(32767); l_http_req utl_http.req; l_http_resp utl_http.resp; l_clob clob; l_text varchar2(32767); begin DBMS_LOB.createtemporary(l_clob, FALSE); utl_http.set_response_error_check(false); l_http_req := utl_http.begin_request(p_url_in, 'POST', 'HTTP/1.1'); utl_http.set_header(l_http_req, 'User-Agent', ' Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0) Gecko/20100101 Firefox/16.0'); 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)); 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; BEGIN LOOP UTL_HTTP.read_text(l_http_resp, l_text, 32766); DBMS_LOB.writeappend(l_clob, LENGTH(l_text), l_text); END LOOP; EXCEPTION WHEN utl_http.end_of_body THEN utl_http.end_response(l_http_resp); END; x_response := l_clob; DBMS_LOB.freetemporary(l_clob); EXCEPTION when others then utl_http.end_response(l_http_resp); DBMS_LOB.freetemporary(l_clob); x_status_code := G_RET_STS_UNEXP_ERROR; x_reason_phrase := sqlerrm; END http_post; /*Main function*/ FUNCTION GET_DASHBOARDS(P_USER_NAME IN VARCHAR2, P_SHOW_HIDDEN IN VARCHAR DEFAULT 'F') RETURN bi_dashboard_array PIPELINED IS l_status_code varchar2(1); l_reason_phrase varchar2(1024); e_expected_err exception; l_url varchar2(1024); l_post_data varchar2(1024); l_receive_data clob; l_db_details_json clob; l_dummy integer; l_scid varchar2(20); l_db_details json; l_itemsinfos_array json_list; l_tmp_obj json; l_tmp_obj_caption json_value; l_tmp_obj_path json_value; l_tmp_obj_attr json_value; BEGIN /*clear all cookies from previous runnings*/ utl_http.clear_cookies; /*call login HTTP request*/ l_url := replace(G_BI_LOGIN_URL, '#ADMINUSER#', G_BI_ADMIN_USER); l_url := replace(l_url, '#ADMINPASSWORD#', G_BI_ADMIN_PASSWORD); l_url := replace(l_url, '#IMPERSONATE#', P_USER_NAME); http_post(p_url_in => l_url, 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 output('LOGIN: status_code = ' || l_status_code); output('LOGIN: reason_phrase = ' || l_reason_phrase); raise e_expected_err; end if; l_dummy := instr(l_receive_data, 'var obips_scid="') + 16; l_scid := substr(l_receive_data, l_dummy, 20); /*call dashboards search HTTP request*/ l_url := G_BI_SEARCH_URL; l_post_data := 'action=search' || -- '&path=%2F' || -- '&mask=*' || -- '&recurse=t' || -- '&followLinks=f' || -- '&withperm=1' || -- '&withpermmask=1' || -- '&sig=' || -- '&compositeSig=dashboardfolder1' || -- '&icharset=utf-8' || -- '&_scid=' || l_scid || -- '&urlGenerator=qualified'; http_post(p_url_in => l_url, p_data_in => l_post_data, p_data_type => 'application/x-www-form-urlencoded', 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 output('SEARCH: status_code = ' || l_status_code); output('SEARCH: reason_phrase = ' || l_reason_phrase); raise e_expected_err; end if; --output(l_receive_data); l_dummy := instr(l_receive_data, '{"itemsinfos"'); l_db_details_json := substr(l_receive_data, l_dummy); /*call logoff HTTP request*/ l_url := G_BI_LOGOFF_URL; http_post(p_url_in => l_url, x_status_code => l_status_code, x_reason_phrase => l_reason_phrase, x_response => l_receive_data); /*parse received JSON data with dashboards details*/ l_db_details := json(l_db_details_json); l_itemsinfos_array := json_list(l_db_details.get('itemsinfos')); for i in 1 .. l_itemsinfos_array.count loop l_tmp_obj := json(l_itemsinfos_array.get(i)); l_tmp_obj_caption := l_tmp_obj.get('caption'); l_tmp_obj_path := l_tmp_obj.get('path'); l_tmp_obj_attr := l_tmp_obj.get('attributes'); if (l_tmp_obj_attr.get_number = G_ItemAttributes_HIDDEN and upper(P_SHOW_HIDDEN) != 'T') then null; else PIPE ROW(bi_dashboard(l_tmp_obj_caption.get_string, l_tmp_obj_path.get_string)); end if; end loop; return; EXCEPTION when e_expected_err then null; --TODO END GET_DASHBOARDS; END BI_UTILS; /
Как вы, наверняка, заметили - в пакете используется парсинг JSON.
Это пакет PL/JSON, который очень просто скачать и установить в вашей рабочей схеме БД.
И, конечно же, не забудьте привести в соответствие со своей средой глобальные переменные пакета
G_BI_ADMIN_USER - логи административной учетной записи (из-под которой будет выполняться имперсонация).
G_BI_ADMIN_PASSWORD - пароль административной учетной записи.
G_BI_BASE_URL - URL доступа к рабочему серверу BI.
3) Если расширение PL/JSON вы установили, скомпилировали без ошибок пакет BI_UTILS, то теперь простым SQL-запросом вы можете получать список инфопанелей, доступных конкретной учетной записи test.
select dashboard_name, dashboard_path from table(bi_utils.get_dashboards('test'))
P.S.
Полагаю, что при необходимости можно усложнить приведенный тут код пакета, чтобы:
- кешировать результаты HTTP-запроса в таблице БД;
- в зависимости от параметра (P_FORCE) обращаться к кешу, при его наличии, либо принудительно обновлять кеш;
- добавить аналогичную функцию для получения списка страниц конкретной информационной панели;
- помимо "caption" и "path" можно использовать и ряд других свойств информационных панелей;
- можно добавить возможность поиска информационных панелей лишь внутри конкретной папки (например, "/shared" )
Этот комментарий был удален автором.
ОтветитьУдалить