I found few examples how to integrate Amazon Alexa with Oracle. All solutions used Amazon Lambda microservice (node.js). But it’s not necessary. You need only two elements: Oracle with ORDS and Amazon Alexa Skill.
Prerequsites:
- Amazon account (https://developer.amazon.com/ )
- Oracle 11g +
- Oracle ORDS (accessed thru SSL)
- APEX (optionaly)
1. For demo I used only one simple table:
You can use these inserts to populate sample datas
On begining enable RESTService on schema layer, my schema name is "aps":
Next create REST module. On Connections pannel expand REST Data Services branch and try to add new module.
After this create Handler.
Finaly connect PLSQL with RESTService. You can see SQL Worksheet with Parameters and Details tabs. (In this place SQLDeveloper is not validating syntax - be careful )
Copy, paste and Save this code into SQL Worksheet:
You can see two parameters :body and :status. These parameters are build in so it's not allowed to define them into Parameters tab.
That's all on Oracle layer!
In the next post on blog I will describe how to prepare ALEXA skills and how to connect all together.
Second part is here:
https://apexutil.blogspot.com/2018/03/oracle-apex-amazon-alexa-integration_28.html
Prerequsites:
- Amazon account (https://developer.amazon.com/ )
- Oracle 11g +
- Oracle ORDS (accessed thru SSL)
- APEX (optionaly)
1. For demo I used only one simple table:
create table alexa_orders
(
alo_id raw(16) default sys_guid() primary key,
alo_name varchar2(4000),
alo_qty number,
alo_value number,
alo_date date,
alo_status varchar2(1024),
alo_ship_date date
);
You can use these inserts to populate sample datas
insert into alexa_orders ( alo_name, alo_qty , alo_value , alo_date, alo_status, alo_ship_date)
values ('Pizza Pepperoni', round(dbms_random.value(1,10),0), round(dbms_random.value(5,1000),2), sysdate-dbms_random.value(1,3),'In progress',sysdate+((1/1440)*dbms_random.value(1,120)));
insert into alexa_orders ( alo_name, alo_qty , alo_value , alo_date, alo_status, alo_ship_date)
values ('Pizza Margarita',round(dbms_random.value(1,10),0), round(dbms_random.value(5,1000),2), sysdate-dbms_random.value(1,3),'Prepared raw',sysdate+(1/1440)*dbms_random.value(1,120));
insert into alexa_orders ( alo_name, alo_qty , alo_value , alo_date, alo_status, alo_ship_date)
values ('Pizza Diabolo',round(dbms_random.value(1,10),0), round(dbms_random.value(5,1000),2), sysdate-dbms_random.value(1,3),'Completed',sysdate+(1/1440)*dbms_random.value(1,120));
insert into alexa_orders ( alo_name, alo_qty , alo_value , alo_date, alo_status, alo_ship_date)
values ('Pizza Pepperoni',round(dbms_random.value(1,10),0), round(dbms_random.value(5,1000),2), sysdate-dbms_random.value(1,3),'Delivered',sysdate+(1/1440)*dbms_random.value(1,120));
insert into alexa_orders ( alo_name, alo_qty , alo_value , alo_date, alo_status, alo_ship_date)
values ('Pizza Diabolo',round(dbms_random.value(1,10),0), round(dbms_random.value(5,1000),2), sysdate-dbms_random.value(1,3),'Delivered',sysdate+(1/1440)*dbms_random.value(1,120));
commit;
2. All Oracle logic I packaged into one PLSQL package:
CREATE OR REPLACE PACKAGE alexa_pkg IS
PROCEDURE parse_alexa_skill (
p_body IN BLOB
);
PROCEDURE add_order (
pr_alexa_orders IN OUT alexa_orders%rowtype
);
END alexa_pkg;
/
create or replace PACKAGE BODY ALEXA_PKG AS
/**
* Transform blob to clob datatype.
* Author: Andrzej Nowakowski
* Since: 28-02-2018
* Version: 1.0.0
*
* @param p_date - blob data
*/
FUNCTION blob_to_clob (p_data IN BLOB)
RETURN CLOB
AS
l_clob CLOB;
l_dest_offset PLS_INTEGER := 1;
l_src_offset PLS_INTEGER := 1;
l_lang_context PLS_INTEGER := DBMS_LOB.default_lang_ctx;
l_warning PLS_INTEGER;
BEGIN
DBMS_LOB.createTemporary(
lob_loc => l_clob,
cache => TRUE);
DBMS_LOB.converttoclob(
dest_lob => l_clob,
src_blob => p_data,
amount => DBMS_LOB.lobmaxsize,
dest_offset => l_dest_offset,
src_offset => l_src_offset,
blob_csid => DBMS_LOB.default_csid,
lang_context => l_lang_context,
warning => l_warning);
RETURN l_clob;
END blob_to_clob;
/**
* Translate date type to Alexa date type.
* Author: Andrzej Nowakowski
* Since: 28-02-2018
* Version: 1.0.0
*
* @param p_date - some date
* @return Alexa date type "Monday, first May"
*/
function say_date (p_date in date)
return varchar2
is
begin
if p_date is null then
return null;
else
return trim(to_char(p_date,'Day'))||'. ????'||trim(to_char(p_date,'MMDD'))||' ';
end if;
end say_date;
/**
* Prepare message for ALEXA session.
* Author: Andrzej Nowakowski
* Since: 28-02-2018
* Version: 1.0.0
*
* @param p_voice_message - what alexa must say
* @param p_card_title - what display on Alexa card title (you can see it on Alexa app)
* @param p_card_content - what display on Alexa card content (you can see it on Alexa app)
*
* @return ALEXA specific message
*/
function alexa_answer ( p_voice_message in varchar2,
p_card_title in varchar2 default 'APEXUTIL',
p_card_content in varchar2 default 'Apex Pizza orders information')
return varchar2
is
l_message varchar2(32000);
begin
l_message := '
{
"version": "1.0",
"sessionAttributes": {},
"response": {
"outputSpeech": {
"type": "SSML",
"ssml": "'||p_voice_message||' "
},
"card": {
"type": "Simple",
"title": "'||p_card_title||'",
"content": "'||p_card_content||'"
},
"shouldEndSession": true
}
}
';
return l_message;
end alexa_answer;
/**
* Prepare data for alexa.
* Author: Andrzej Nowakowski
* Since: 28-02-2018
* Version: 1.0.0
*
* @param p_values - ALEXA'a JSON message
* @param p_json_count - quantity of alexa variables
* @param p_voice_message - what alexa must say
* @param p_card_title - what display on Alexa card title (you can see it on Alexa app)
* @param p_card_content - what display on Alexa card content (you can see it on Alexa app)
*/
procedure message_howmanyorders(p_values in apex_json.t_values,
p_json_count in number,
p_voice_message in out varchar2,
p_card_title in out varchar2,
p_card_content in out varchar2)
is
l_slots_name varchar2(4000);
l_slot_value varchar2(4000);
l_return_howmanyorders number;
begin
for x in 1.. p_json_count
loop
l_slots_name := apex_json.get_members(p_path=>'request.intent.slots',p_values=>p_values)(x) ;
l_slot_value := apex_json.get_varchar2(p_path=>'request.intent.slots.'||l_slots_name||'.value',p0=>x,p_values=>p_values);
if l_slot_value is not null then
if REGEXP_COUNT(l_slot_value, '-')=2 then --ex. today, now, tomorrow, november twenty-fifth, next monday, right now
select count(*) into l_return_howmanyorders from alexa_orders where trunc(alo_date,'DD') = to_date(l_slot_value,'YYYY-MM-DD');
elsif REGEXP_COUNT(l_slot_value, '-')=1 and REGEXP_COUNT(l_slot_value, 'W')= 0 then --this month, last month
select count(*) into l_return_howmanyorders from alexa_orders where to_char(trunc(alo_date,'DD'),'YYYY-MM') = l_slot_value;
elsif REGEXP_COUNT(l_slot_value, '-')=1 and REGEXP_COUNT(l_slot_value, 'W')= 1 then --ex. this week, next week
select count(*) into l_return_howmanyorders from alexa_orders where to_char(trunc(alo_date,'DD'),'YYYY-"W"IW') = l_slot_value;
elsif REGEXP_COUNT(l_slot_value, '-')=0 then --ex. this yesr, last year
select count(*) into l_return_howmanyorders from alexa_orders where to_char(alo_date,'YYYY') = l_slot_value;
elsif REGEXP_COUNT(l_slot_value, '-')=1 and REGEXP_COUNT(l_slot_value, 'W')= 0 then --this month, last month
null;
end if;
p_voice_message := 'Apex Pizza processed '||l_return_howmanyorders||' orders';
else
p_voice_message := 'Please say again';
end if;
end loop;
p_card_content := p_voice_message;
end message_howmanyorders;
/**
* Main procedure. Parse ALEXA data and return answer
* Author: Andrzej Nowakowski
* Since: 28-02-2018
* Version: 1.0.0
*
* @param p_values - ALEXA'a JSON message
* @param p_json_count - quantity of alexa variables
* @param p_voice_message - what alexa must say
* @param p_card_title - what display on Alexa card title (you can see it on Alexa app)
* @param p_card_content - what display on Alexa card content (you can see it on Alexa app)
*/
procedure parse_alexa_skill (p_body in blob)
is
l_body_clob clob;
l_values apex_json.t_values;
l_intent_name varchar2(4000);
l_json_count number;
l_voice_message varchar2(4000);
l_card_title varchar2(4000);
l_card_content varchar2(4000);
begin
--transfer blob to clob
l_body_clob := blob_to_clob (p_data => p_body);
--Parse clob to json object
apex_json.parse(l_values,l_body_clob);
l_intent_name := apex_json.get_varchar2(p_path=>'request.intent.name',p0=>1,p_values=>l_values);
--Check how many rows in json object
l_json_count := apex_json.get_count
(
p_path => 'request.intent.slots',
p_values => l_values
);
if l_intent_name in ('howmanyorders') then
message_howmanyorders( p_values => l_values,
p_json_count => l_json_count,
p_voice_message => l_voice_message,
p_card_title => l_card_title,
p_card_content => l_card_content);
htp.p(alexa_answer (p_voice_message => l_voice_message,
p_card_title => l_card_title,
p_card_content => l_card_content));
end if;
end parse_alexa_skill;
procedure add_order (pr_alexa_orders in out alexa_orders%rowtype) AS
BEGIN
-- TODO: Implementation required for procedure ALEXA_PKG.add_order
NULL;
END add_order;
END ALEXA_PKG;
In the package you can find:- BLOB_TO_CLOB function - Used to convert http response to readable clob
- SAY_DATE function - Returning date in Alexa format (very specific)
- ALEXA_ANSWER function - Used to prepare ALEXA JSON message
- MESSAGE_HOWMANYORDERS procedure - Preparing information from ALEXA_ORDERS table (in Oracle)
- PARSE_ALEXA_SKILL procedure - Used as a main API. It will be used by RESTService
3. Next prepare REST web service. You can use APEX, SQL Developer or PLSQL API. In this case SQLDeveloper is used.
Remember, to create webservice you have to use ORDS 3.08 or newest.On begining enable RESTService on schema layer, my schema name is "aps":
Enable RESTService on schema layer STEP 1 |
Enable RESTService on schema layer STEP 2 |
Enable RESTService on schema layer STEP 3 |
Next create REST module. On Connections pannel expand REST Data Services branch and try to add new module.
Add REST module. |
Add REST module. STEP 1 |
Add REST module. STEP 2 |
Add REST module. STEP 3 |
After this create Handler.
Add Handler |
Add Handler Next step |
Finaly connect PLSQL with RESTService. You can see SQL Worksheet with Parameters and Details tabs. (In this place SQLDeveloper is not validating syntax - be careful )
Copy, paste and Save this code into SQL Worksheet:
begin
ALEXA_PKG.parse_alexa_skill (p_body => :body);
:status := 200;
end;
You can see two parameters :body and :status. These parameters are build in so it's not allowed to define them into Parameters tab.
That's all on Oracle layer!
In the next post on blog I will describe how to prepare ALEXA skills and how to connect all together.
Second part is here:
https://apexutil.blogspot.com/2018/03/oracle-apex-amazon-alexa-integration_28.html
Excellent posting.
ReplyDeletecan u plz tell with echo gen ur using?
ReplyDeleteI tried with Echo and Echo2
Delete