Oracle Apex Getting JSON.WRITER.NOT_OPEN Error - oracle-apex

I tried the following code in SQL workshop and it ran. I tried the same code in an application after removing DBMS_OUT_PUT using it in a set value action and I get the error message: Ajax call returned server error ORA-20987: APEX - JSON.WRITER.NOT_OPEN - Contact your application administrator.
Details about this incident are available via debug id "354017". for Set Value. However, the debug logs don't provide any additional details.
What am I missing? It worked in the application a few days ago.
declare
l_request_url varchar2(32767);
l_content_type varchar2(32767);
l_content_length varchar2(32767);
l_response varchar2(10000);
l_body_clob clob;
l_name varchar2(200);
download_failed_exception exception;
j apex_json.t_values;
n_customer_id number;
begin
l_request_url := 'https://mysterysite.com/api/v3/customer';
APEX_JSON.initialize_clob_output;
APEX_JSON.open_object;
APEX_JSON.write('company', :P5_COMPANY);
APEX_JSON.write('bill_addr1', :P5_BILL_ADDR1);
APEX_JSON.write('bill_addr2', :P5_BILL_ADDR2);
APEX_JSON.write('bill_city', :P5_BILL_CITY);
APEX_JSON.write('bill_postcode', :P5_BILL_POSTCODE);
APEX_JSON.write('superuser_email', :P5__YOUR_EMAIL);
APEX_JSON.close_object;
l_body_clob :=APEX_JSON.get_clob_output;
APEX_JSON.free_output;
apex_web_service.g_request_headers.delete();
apex_web_service.g_request_headers(1).name := 'Content-Type';
apex_web_service.g_request_headers(1).value := 'application/json';
l_response := apex_web_service.make_rest_request(
p_url => l_request_url
, p_http_method => 'POST'
, p_username => 'junk username'
, p_password => 'junkpassword'
, p_body => l_body_clob
);
if apex_web_service.g_status_code != 201 then
DBMS_OUTPUT.PUT_LINE('ERROR');
else
apex_json.Parse (j, l_response);
n_customer_id := apex_json.get_varchar2 (p_values => j, p_path => 'response.id');
DBMS_OUTPUT.PUT_LINE(n_customer_id);
end if;
end;
Product Build 22.2.1
Schema Compatibility 2022.10.07
Patch Version 1
Last Patch Time 12/26/2022 05:32:28 PM
Last DDL Time 12/26/2022 04:37:11 PM
Host Schema ORDS_PLSQL_GATEWAY
Application Owner APEX_220200

Not a complete solution, just a work around.
Replace the set value action with execute server side code action. In the code replace RETURN with apex_util.set_session_state ('item_name', value);

Try to set the preserve parameter to true in the call to initialize_clob_output.
In what context are you running this code? Another process probably also uses apex_json in the same code.

Related

Get path param value into ORDS prehook

Is there a way to get pathparam from url with get_cgi_env? Example: https://clientes/{codigo}. I would like to get the value from :codigo pathparam.
Into handler GET, POST, PUT, etc. on endpoint it is possible to get the pathparam value but it's not clear how can to be done into ORDS prehook.
I have this function to get the complete url - the "QUERY_STRING" portion should give you the parameters
FUNCTION request_url RETURN VARCHAR2
IS
l_url VARCHAR2(1024);
BEGIN
l_url := owa_util.get_cgi_env('SERVER_NAME') ||':' ||
owa_util.get_cgi_env('SERVER_PORT') ||
owa_util.get_cgi_env('SCRIPT_NAME') ||
owa_util.get_cgi_env('PATH_INFO') ||
owa_util.get_cgi_env('QUERY_STRING');
RETURN l_url;
EXCEPTION WHEN VALUE_ERROR THEN
RETURN 'unable to retrieve request_url';
END request_url;
You can access all the CGI variables available via owa.cgi_var_name and owa.cgi_var_val (owa.num_cgi_vars for the count).
I have this running in my preHook:
pragma autonomous_transaction;
l_obj json_object_t := json_object_t();
l_json blob;
begin
for i in 1 .. owa.num_cgi_vars loop
l_obj.put(owa.cgi_var_name(i), owa.cgi_var_val(i));
end loop;
l_json := l_obj.to_blob;
insert into rest_access
(id, created, cgi_variables, current_schema)
values
(s_rest_access.nextval, sysdate, l_json, sys_context('userenv', 'current_schema'));
commit;
Lots of values that you have access to, including parameters.
To see a list of parameters after running it and logging requests, simply run
select *
from json_table((select json_dataguide(cgi_variables, dbms_json.format_flat) from rest_access),
'$[*]' columns( --
opath varchar2(200) path '$."o:path"',
otype varchar2(40) path '$.type',
olength number path '$."o:length"')) j;

How to URL Encode Page Item Correctly - APEX 21.1

I have a RESET_PSWD button on a Form that calls a dynamic action with 4 true actions defined.
Confirm Action
Execute Server-side Code - PL/SQL procedure to generate new temp pswd
Execute Server-side Code - PL/SQL procedure to generate modal url using APEX_PAGE.GET_URL
Execute JavaScript Code - use url from step 3 to open modal
The goal of the DA is to generate a Temp Password, build a URL to the modal (passing in the new Temp Password from Step 2), and then navigate to the modal. Between Step 2 and Step 3 the value in P7_TEMP_PSWD is getting truncated due to special characters not being url encoded correctly between the request calls.
-- package procedure to get & set url for confirmation modal (Step 3)
PROCEDURE get_feedback_url (
p_username VARCHAR2,
p_temp_pswd VARCHAR2
) IS
vcPswdEncoded VARCHAR2(1000);
vcURL VARCHAR2(1000);
vcValueList VARCHAR2(1000);
BEGIN
vcPswdEncoded := APEX_UTIL.URL_ENCODE(p_temp_pswd);
vcValueList := p_username ||','|| vcPswdEncoded;
vcURL := APEX_PAGE.GET_URL(p_page => '12',
p_items => 'P12_USERNAME,P12_TEMP_PSWD',
p_values => vcValueList,
p_plain_url => TRUE);
APEX_UTIL.SET_SESSION_STATE('P7_FEEDBACK_URL', vcURL);
EXCEPTION
WHEN OTHERS THEN
csa.rsn_logger.log_error(p_rsn_app => 'RSNUM',
p_app_loc => 'rsnum_user.get_feedback_url',
p_log_msg => SQLERRM);
END;
I am trying to url encode the temp pswd before building the url, but I am getting the following results:
The value after using APEX_UTIL.ENCODE_URL is what I want, but it seems like APEX_PAGE.GET_URL is also encoding the values so '%' is encoded again. Is there a way to opt out of GET_URL doing this? The reason why I am using ENCODE_URL in the first place is because APEX does was not encode '#' (and a few other characters) by default and that was causing me problems originally.
Wish there was a better way to do this, but figured out a working solution (will need to test edge cases, but seems to work). I created a Before Header computation on the modal page with the following expression:
UTL_URL.UNESCAPE(:P12_TEMP_PSWD)

How to call Jira Rest API from Oracle APEX Page

Requirement -
Below fields to be displayed on a APEX page for any Jira issue created.
Issue Number/Issue Id
Summary
Description
Status
Assignee
Example, user01 and user02 both having access to Oracle APEX and Jira. user01 created 3 issues and user02 created 5 issues in Jira. Now when user01 who logs into Oracle APEX, should see 3 issues and when user02 who logs into Oracle APEX, should see 5 issues on a APEX page.
https://jira.local.com/jira/rest/api2
I'm new to APEX Rest service call and request to all of you to provide some sample APEX code to call Jira REST API using username/password for the above requirement.
Thanks all.
I have done something similar before. In my case, the goal was to get all the issues of project PRJ001 (Jira Project Key) from Jira and store them in a local database table (J_ISSUES), periodically, so I've created a Job that executed the following procedure from time to time:
create or replace PROCEDURE "JIRA_SYNC_ISSUES" as
var_response CLOB;
var_counter number := 0;
var_total number := 1;
var_prj_key varchar2(10);
begin
var_prj_key := 'PRJ001';
var_total := 1;
var_counter := 0;
while var_total > var_counter
loop
apex_session.create_session (p_app_id => 107, p_page_id => 1, p_username => 'USERNAME01');
var_response:= apex_web_service.make_rest_request(
p_url => 'https://yourcompany.atlassian.net/rest/api/2/search?jql=project="'|| var_prj_key ||'"&maxResults=100&startAt=' || var_counter || '',
p_http_method => 'GET',
p_credential_static_id => 'oud_rest'
);
SELECT JSON_VALUE(var_response, '$.total' RETURNING NUMBER) into var_total
FROM DUAL;
for i in (SELECT *
FROM
JSON_TABLE(var_response, '$.issues[*]'
COLUMNS (
key VARCHAR2(100) PATH '$.key',
prj_key VARCHAR2(100) PATH '$.fields.project.key',
status VARCHAR2(100) PATH '$.fields.status.name',
updated VARCHAR2(100) PATH '$.fields.updated',
assignee VARCHAR2(100) PATH '$.fields.assignee.name',
time_spent NUMBER PATH '$.fields.timespent'
)))
loop
begin
insert into j_issues (jis_key, jis_jpr_key, jis_status, jis_updated, jis_assignee, jis_time_spent)
values (i.key,
i.prj_key,
i.status,
to_timestamp(substr(i.updated,1,10)||' '||substr(i.updated,12,8),'YYYY-MM-DD HH24:MI:SS'),
i.assignee,
i.time_spent);
exception
when dup_val_on_index then
update j_issues
set jis_jpr_key = i.prj_key,
jis_status = i.status,
jis_updated = to_timestamp(substr(i.updated,1,10)||' '||substr(i.updated,12,8),'YYYY-MM-DD HH24:MI:SS'),
jis_assignee = i.assignee,
jis_time_spent = i.time_spent
where jis_key = i.key;
end;
var_counter := var_counter + 1;
end loop;
end loop;
end jira_sync_issues;
In the first part it is authenticating and making the call to the API. In the second, it takes the var_response and inserts it into the required table.
For the first part, I've read this: https://www.jmjcloud.com/blog/simplifying-apex_web_service-oauth2-client-credentials
For the second: https://docs.oracle.com/database/121/SQLRF/functions092.htm
Notice the var_counter and var_total variables. They are used to iterate through the Jira responses, because there is a limit of items being retrieved in each response. So, if you are expecting more than that limit, you should make more calls to retrieve them.
If the volume of issues is big, there are advantages in doing a scheduled sync like this instead of letting the user wait for the entire result. After the data is in your database, you can just query it as usual.
Let me know if it helps ;)

Oracle APEX Set value to Page Item from Ajax process

I'm trying to upload document from APEX page to S3 bucket and I'm successful with the help of plugin from https://www.apexutil.com/ords/prod/f?p=700:200:1349309139567:::::
The plugin demonstrate retrieve the upload result and inserting to table as explained below.
Create new dynamic action;
Choose Upload Success [FM Component];
Choose "Execute JavaScript Code" action;
Inside the "Code" textarea define code:
apex.server.process("my_ajax_process", {
x01: this.browserEvent.originalEvent.detail.serverId,
x02: this.browserEvent.originalEvent.detail.file.name,
x03: this.browserEvent.originalEvent.detail.file.body.size,
x04: this.browserEvent.originalEvent.detail.file.body.type
}, {
success: function() {
console.log("success");
},
and retrieve the x0 values in Ajax process and inserting to table.
Create Ajax Process, name: "my_ajax_process"
Define PL/SQL code:
declare
l_server_id varchar2(4000) := apex_application.g_x01;
l_name varchar2(4000) := apex_application.g_x02;
l_size number := apex_application.g_x03;
l_type varchar2(4000) := apex_application.g_x04;
begin
insert into MY_TABLE (MT_SERVER_ID, MT_NAME, MT_SIZE, MT_TYPE)
values (l_server_id, l_name, l_size, l_type);
owa_util.status_line(nstatus => 204, creason => 'No Content');
end;
However I need to capture the X0 values in the Ajax process and assign to Form pages items, as I have to store the output along with other form fields and store into my table. I have tried and however its not getting updated. Any help much appreciated
declare
l_server_id varchar2(4000) := apex_application.g_x01;
l_name varchar2(4000) := apex_application.g_x02;
l_size number := apex_application.g_x03;
l_type varchar2(4000) := apex_application.g_x04;
begin
:P12_DOCUMENTURL := l_server_id;
:P12_DOCUMENTTYPE := l_type;
:P12_DOCSIZE := l_size;
owa_util.status_line(nstatus => 204, creason => 'No Content');
end;
:P12_DOCUMENTURL := l_server_id; -- This kind of assignment wont work in an ajax process
But you can use APEX_UTIL.SET_SESSION_STATE
BEGIN
APEX_UTIL.SET_SESSION_STATE('my_item','myvalue');
END;
Example
BEGIN
APEX_UTIL.SET_SESSION_STATE('P12_DOCUMENTURL',l_server_id);
END;
More details here -
https://docs.oracle.com/cd/E37097_01/doc.42/e35127/GUID-62AA4333-160D-44FD-9F07-D188A2F4BC55.htm#AEAPI181
Also please take a look at this https://jeffkemponoracle.com/2014/02/apex_util-set_session_state-may-or-may-not-commit/
For some more discussion about the commits issued.
Finally this works for me.
A dynamic action
var l01 = this.browserEvent.originalEvent.detail.serverId;
var l03 = this.browserEvent.originalEvent.detail.file.body.size;
var l04 = this.browserEvent.originalEvent.detail.file.body.type;
apex.server.process
( "upload_ajax_process",
{},
{ success: function( pData ) {
$s("P12_DOCUMENTURL",l01 );
$s("P12_DOCSIZE", l03);
$s("P12_DOCUMENTTYPE", l04);
}
}
);
Also Ajax process "upload_ajax_process" with some dummy statement. Without this I'm getting "Error: SyntaxError: Unexpected token P in JSON at position 0"
begin
owa_util.status_line(nstatus => 204, creason => 'No Content');
end;

APEX - how to call store procedure base on values of items on the page

similiar problem has already been mentioned, described and solved here using dynamic actions but I still can't implement it in my case.
I have a form (created authomatically but page creator) to change password in remote database. There are three items
login : P15_UNAME (select list) LOV
new password: P15_NEW (password)
button to execute: SUBMIT (button).
Button fires a simple stored procedure:
declare
success int;
msg varchar(100);
begin
SYS.CHANGEPASSWORD#abc(
PUSERNAME => :P15_UNAME,
PNEWPASSWORD => :P15_NEW,
PRESULT => success,
PMESSAGE => msg);
if success = 0 then
apex_application.g_print_success_message := msg;
else
apex_application.g_print_success_message := '<span style="color:red">' || msg || '</span>';
end if ;
end ;
Unfortunately choosen/typed values of login and password are not called by stored procedure. I probably should use dynamic action but have no idea how to call store procedure and dynamic simultaneously. Could you give me some hints please.
K.
Are you trying to pass the page item values directly into the database? Your procedure / procedure call should look something like this.
-- database
PROCEDURE p_change_details(p_uname varchar2, p_password varchar2)
IS
success int;
msg varchar(100);
begin
SYS.CHANGEPASSWORD#abc(
PUSERNAME => p_uname,
PNEWPASSWORD => p_password,
PRESULT => success,
PMESSAGE => msg);
if success = 0 then
apex_application.g_print_success_message := msg;
else
apex_application.g_print_success_message := '<span style="color:red">' || msg || '</span>';
end if ;
end ;
--APEX
p_change_details(:P15_UNAME, :P15_NEW)
You can create a Dynamic Action to execute a PL/SQL code on button click.
refer this example- Link
Hope this will help you. Let me know for any question.