Artikkel to om Oracle REST Data Services viser hvordan du lett kan lage et tilpasset REST API på egne data i Oracle.
Skreddersydd REST API med ORDS
Første del i denne serien introduserte Oracle REST Data Services (ORDS) og viste hvordan man lager et REST API på tabeller og views med noen få klikk i SQL Developer. I denne delen brukes PL/SQL-pakken ORDS til å definere et API med relativt få linjer med PL/SQL. Det som vises her forutsetter at man har ORDS installert og kjørende som vist i første del.
Til senere når du trenger å laste ned ORDS, dokumentasjon, ideer eller hjelp i et brukerforum er det lettest å huske URL oracle.com/ords .
To tabeller er hentet og forenklet fra et timeregistreringssystem:
SQL for tabellene
CREATE TABLE prosjekt (
id NUMBER
GENERATED ALWAYS AS IDENTITY,
CONSTRAINT prosjekt_pk PRIMARY KEY ( id ),
navn VARCHAR2(100) NOT NULL,
beskrivelse VARCHAR2(2000),
starttid DATE DEFAULT sysdate NOT NULL,
avsluttet DATE,
guid RAW(16) DEFAULT sys_guid()
);
CREATE TABLE aktivitet (
id NUMBER
GENERATED ALWAYS AS IDENTITY,
CONSTRAINT aktivitet_pk PRIMARY KEY ( id ),
navn VARCHAR2(100) NOT NULL,
beskrivelse VARCHAR2(2000),
prosjekt_id NUMBER,
CONSTRAINT aktivitet_prosjekt_fk FOREIGN KEY ( prosjekt_id )
REFERENCES prosjekt,
guid RAW(16) DEFAULT sys_guid()
);
Hvorfor ikke AutoREST?
Det er selvsagt mulig å legge et enkelt REST API på disse tabellene som vist i forrige post, men typisk vil man ha mer kontroll på sakene og tilpasninger ved å legge et API foran (eller på toppen, alt etter hvordan du ser for deg dette.) Det bør også sies at man kan se for seg mange veier inn til disse tabellene og et REST API er bare en av flere. Med et felles API som alle andre integrasjoner må gå gjennom vil vi få god kontroll. Derfor lages det her et skall i form av PL/SQL-pakker som vårt REST API kaller på. Det er anbefalt en mer avansert sikkerhetsarkitektur med flere skjema, hvor tabeller ligger et sted, og kode i et annet med utvalgte rettigheter på tabellene i det første. Det blir ikke vist her.
Det er også mulig å benytte AutoREST på PL/SQL-pakker, men da blir det automatisk generert endepunkter og kun POST operasjoner tillatt. Metoden som vises her gir god fleksibilitet for relativt lite ekstra kode.
API fra Oracle i form av PL/SQL-pakken ORDS er beskrevet i Developer’s Guide , dvs i kapittel 4
Aktivere REST for et skjema
For et nytt skjema (databasebruker) må ORDS aktiveres, for den brukeren man er pålogget som kan det gjøres med:
begin
ords.enable_schema;
commit;
end;
/
Da blir brukernavn en del av URL og det vil man gjerne ikke vise, så vi bruker admin
her:
begin
ords.enable_schema(p_url_mapping_pattern => 'admin');
commit;
end;
/
Det er flere parametre man kan se på, blant annet kan en bruker med DBA-rolle aktivere for annen bruker ved hjelp av parameter p_schema
og man kan deaktivere det helt ved å sette p_enabled
til false
.
Module, Template, Handler og Parameter
Fire begreper er lurt å lære seg, de har en naturlig hierarkisk orden:
- module - dette vil typisk være navn på API, feks timereg i dette tilfellet og utgjør del av url etter
/ords/
. - template - tilsvarer del av url som angår et objekt, feks prosjekt, aktivitet, etc
- handler - For hvert objekt har man HTTP operasjoner (verb) man tillater (GET, POST, PUT, DELETE). Handler er koden som svarer på en HTTP request mot en URL.
- parameter - Tilknyttes en handler. Disse mapper innsendt JSON til parametre i kode som skal kjøres, og fra retur-parametre som sendes ut i JSON.
Hele REST API som bygges opp her defineres i en anonym PL/SQL blokk, men her vises de ulike trinnene individuelt etter hvert som vi går frem. Koden er idempotent, dvs du kan kjøre den flere ganger og den vil overskrive eventuelle tidlige konfigurasjoner for samme modul:
Vi lager en modul som kalles timereg ved hjelp av ords.define_module
.
BEGIN
ords.define_module(
p_module_name => 'timereg',
p_base_path => '/timereg/',
p_items_per_page => 25,
p_status => 'PUBLISHED',
p_comments => 'REST API til timeregistrering'
);
END;
I API-et defineres en template for håndtering av prosjekt med rutinen ords.define_template
:
ords.define_template(
p_module_name => 'timereg',
p_pattern => '/prosjekt/',
p_comments => 'Administrasjon av prosjekt');
Senere vil man lage egne templates for å håndtere individuelle prosjekt og aktiviteter til hvert prosjekt.
Til slutt defineres handlers som håndterer HTTP-kall som kommer inn. I første omgang definerer vi kun for GET:
Kode for å definere handler til GET på alle prosjekter kan se slik ut:
begin
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/',
p_method => 'GET',
p_source_type => ords.source_type_collection_feed,
p_source => 'select navn, beskrivelse,starttid, avsluttet,guid from prosjekt;',
p_comments => 'Returnerer alle prosjekt.');
end;
Komplett kode så langt er:
begin
ords.enable_schema(p_url_mapping_pattern => 'admin');
ords.define_module(
p_module_name => 'timereg',
p_base_path => '/timereg/',
p_comments => 'REST API til timeregistrering'
);
ords.define_template(
p_module_name => 'timereg',
p_pattern => '/prosjekt/',
p_comments => 'Administrasjon av prosjekt');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/',
p_method => 'GET',
p_source_type => ords.source_type_collection_feed,
p_source => 'select navn, beskrivelse,starttid, avsluttet, rawtohex(guid) "uid" from prosjekt',
p_comments => 'Returnerer alle prosjekt.');
commit;
end;
/
Om vi legger inn en rad manuelt i tabellen kan vi teste at en vanlig GET fungerer:
curl rio:8080/ords/admin/timereg/prosjekt/ | jq
En forklaring på url over: ORDS kjøres på en lab-server rio på port 8080. ORDS servlet er satt opp med context root /ords
som er standard. /admin
er base path tilknyttet skjema som definert i ords.enable_schema
, /timereg
er definert i ords.define_module
og /prosjekt
er definert i ords.define_template
Eksempel på svar fra ORDS:
{
"items": [
{
"navn": "Timeregistrering applikasjon",
"beskrivelse": "Ny applikasjon for registrering av timer",
"starttid": "2024-04-27T14:36:27Z",
"avsluttet": null,
"guid": "FxQWW/CnCdTgY2QCAArGLg=="
}
],
"hasMore": false,
"limit": 25,
"offset": 0,
"count": 1,
"links": [
{
"rel": "self",
"href": "http://rio:8080/ords/admin/timereg/prosjekt/"
},
{
"rel": "describedby",
"href": "http://rio:8080/ords/admin/metadata-catalog/timereg/prosjekt/"
},
{
"rel": "first",
"href": "http://rio:8080/ords/admin/timereg/prosjekt/"
}
]
}
PL/SQL API
En pakke er laget for å legge inn, oppdatere og slette data. Den har p.t. følgende publiserte rutiner:
CREATE OR REPLACE PACKAGE prosjekt_adm AS
FUNCTION nytt_prosjekt ( -- Denne funksjonen brukes ikke fra REST
p_navn IN prosjekt.navn%TYPE,
p_beskrivelse IN prosjekt.beskrivelse%TYPE,
p_starttid IN prosjekt.starttid%TYPE DEFAULT sysdate,
p_avsluttet IN prosjekt.avsluttet%TYPE DEFAULT NULL
) RETURN prosjekt.id%TYPE;
PROCEDURE nytt_prosjekt (
p_navn IN prosjekt.navn%TYPE,
p_beskrivelse IN prosjekt.beskrivelse%TYPE,
p_starttid IN prosjekt.starttid%TYPE DEFAULT sysdate,
p_avsluttet IN prosjekt.avsluttet%TYPE DEFAULT NULL,
p_guid OUT VARCHAR2
);
PROCEDURE oppdater_prosjekt (
p_guid IN VARCHAR2,
p_navn IN prosjekt.navn%TYPE,
p_beskrivelse IN prosjekt.beskrivelse%TYPE,
p_starttid IN prosjekt.starttid%TYPE,
p_avsluttet IN prosjekt.avsluttet%TYPE DEFAULT NULL
);
PROCEDURE slett_prosjekt (
p_guid IN VARCHAR2
);
END prosjekt_adm;
Resten av koden til pakkene (package body) ligger nederst. Det må bemerkes at dette er langt fra produksjonsklart. Ved å bruke PL/SQL her kan man forbedre feilsjekk og implementasjon forøvrig selv om et fast API ivaretas. Man kan også skreddersy feilmeldinger som blir sendt tilbake i JSON-format.
Flere handlers
En handler for POST-operasjon opprettes slik:
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/',
p_method => 'POST',
p_source_type => ords.source_type_plsql,
p_source => q'[
begin
prosjekt_adm.nytt_prosjekt (
p_navn => :navn,
p_beskrivelse => :beskrivelse,
p_starttid => :starttid,
p_avsluttet => :avsluttet,
p_guid => :uid);
end;]',
p_comments => 'Oppretter et prosjekt.');
ords.define_parameter(
p_module_name => 'timereg',
p_pattern => '/prosjekt/',
p_method => 'POST',
p_name => 'uid' ,
p_bind_variable_name => 'uid',
p_source_type => 'RESPONSE',
p_param_type => 'STRING',
p_access_method => 'OUT',
p_comments =>'UID returned from database.');
Siden vi ønsker å få ut uid
må den defineres som vist med ords.define_parameter
. De andre bind-variablene blir implisitt definert.
Første POST
Følgende json dokument legges i en fil med navn prosjekt.json
:
{
"navn": "Testprosjekt",
"beskrivelse": "Prosjekt for all testing"
}
Dette kan sendes inn med feks curl
:
curl -X POST -H 'Content-Type: application/json' -d@prosjekt.json rio:8080/ords/admin/timereg/prosjekt/
Verdt å merke seg at header med Content-Type: application/json
må være med som vist over, ellers får man feilmelding som kan være vanskelig å finne ut av; innsendt JSON blir ikke riktig tolket og bind-variablene blir ikke definert.
Respons er en kort JSON som inneholder uid:
{"uid":"1716D650145C0DF0E0636402000AE8A2"}
uid
fra respons bruker vi senere når vi skal opprette aktiviteter.
Parametre og datohåndtering
Merk at ikke alle parametre er obligatoriske, PL/SQL-rutinene er laget for å håndtere parametre med standardverdier når de mangler. For de som blir sendt i JSON-format vil ORDS definere dem automagisk. Unntak er om det benyttes navn som ikke er lovlige i PL/SQL.
Om vi ønsker å sende inn starttid, så håndterer ORDS ISO-8601 på strak arm:
{
"navn": "Testprosjekt II",
"beskrivelse": "Prosjekt for enda mer testing",
"starttid": "2024-04-27T14:36:27Z"
}
Det er ikke nødvendig med konvertering med to_date
noe sted i koden for at dette skal fungere.
ORDS gjør commit
I koden som betjener hvert endepunkt og operasjon ligger det ikke eksplisitt commit;
ORDS gjør en commit tilslutt om alt går bra - noe som gir mening siden HTTP er en stateless protokoll.
Operasjoner på individuelt prosjekt
Neste trinn er å definere operasjoner for å hente, oppdatere og slette et individuelt prosjekt. De fleste som bruker et REST API vil forvente å kunne gjøre det mot en URL som vist over med UID lagt til. Følgende oppretter en ny template og tre handlers for GET, PUT og DELETE:
ords.define_template(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid',
p_comments => 'Administrasjon av prosjekt');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid',
p_method => 'GET',
p_source_type => ords.source_type_collection_item,
p_source => q'[
select navn, beskrivelse,starttid, avsluttet, rawtohex(guid) "uid"
from prosjekt
where guid=hextoraw(:uid)]',
p_comments => 'Returnerer et prosjekt.');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid',
p_method => 'PUT',
p_source_type => ords.source_type_plsql,
p_source => q'[
begin
prosjekt_adm.oppdater_prosjekt(
p_guid => :uid,
p_navn => :navn,
p_beskrivelse => :beskrivelse,
p_starttid => :starttid,
p_avsluttet => :avsluttet);
end;]',
p_comments => 'Oppdaterer et prosjekt.');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid',
p_method => 'DELETE',
p_source_type => ords.source_type_plsql,
p_source => q'[
begin
prosjekt_adm.slett_prosjekt (
p_guid => :uid);
end;]',
p_comments => 'Sletter et prosjekt.');
Med dette lagt til kan man hente et enkelt prosjekt, oppdatere det og slette det med hhv:
curl rio:8080/ords/admin/timereg/prosjekt/1716D650145E0DF0E0636402000AE8A2
curl -X PUT -H "Content-Type: application/json" -d@prosjekt_put.json \
rio:8080/ords/admin/timereg/prosjekt/1716D650145E0DF0E0636402000AE8A
curl -X DELETE rio:8080/ords/admin/timereg/prosjekt/1716D650145E0DF0E0636402000AE8A2
Parameter uid
er implisitt definert gjennom p_pattern
i ords.define_handler
.
Source type
De ulike handlers har forskjellig p_source_type
som må settes iht koden som kjøres:
ords.source_type_collection_feed
- Select som returnerer flere rader blir kjørt, ORDS returnerer JSON arrayords.source_type_collection_item
- Select som returnerer en rad, ORDS returnerer et objekt.ords.source_type_plsql
- PL/SQL kjøres og ORDS mapper IN og OUT parametre.
Aktiviteter til et prosjekt
For å håndtere aktiviteter er det laget en egen forenklet pakke for det. Her inneholder den kun en rutine for å opprette en ny aktivitet:
CREATE OR REPLACE PACKAGE aktivitet_adm AS
PROCEDURE ny_aktivitet (
p_navn IN aktivitet.navn%TYPE,
p_beskrivelse IN aktivitet.beskrivelse%TYPE,
p_prosjekt_guid IN VARCHAR2,
p_guid OUT VARCHAR2
);
END aktivitet_adm;
/
Siden en aktivitet er knyttet til et bestemt prosjekt vil det være naturlig å legge /aktivitet/
bakerst på URL til et bestemt prosjekt. Følgende kode oppretter template og to handlers for å liste ut alle aktiviteter til et prosjekt og opprette en ny aktivitet knyttet til et bestemt prosjekt:
ords.define_template(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid/aktivitet/',
p_comments => 'Administrasjon av aktiviteter til et prosjekt');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid/aktivitet/',
p_method => 'GET',
p_source_type => ords.source_type_collection_feed,
p_source => q'[
select p.navn prosjektnavn, a.navn aktivitetsnavn,
a.beskrivelse aktivitetsbeskrivelse,
rawtohex(p.guid) prosjekt_uid, rawtohex(a.guid) aktivitet_uid
from prosjekt p join aktivitet a on p.id = a.prosjekt_id
where p.guid=hextoraw(:uid)]',
p_comments => 'Returnerer alle aktiviteter for prosjekt.');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid/aktivitet/',
p_method => 'POST',
p_source_type => ords.source_type_plsql,
p_source => q'[
begin
aktivitet_adm.ny_aktivitet (
p_navn => :navn,
p_beskrivelse => :beskrivelse,
p_prosjekt_guid => :uid,
p_guid => :aktivitet_uid);
end;]',
p_comments => 'Oppretter en aktivitet for prosjekt.');
ords.define_parameter(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid/aktivitet/',
p_method => 'POST',
p_name => 'aktivitet_uid' ,
p_bind_variable_name => 'aktivitet_uid',
p_source_type => 'RESPONSE',
p_param_type => 'STRING',
p_access_method => 'OUT',
p_comments =>'UID til opprettet aktivitet.');
Eksempel på POST som oppretter en ny aktivitet for et prosjekt:
curl -X POST -H 'Content-Type: application/json' -d@aktivitet.json rio:8080/ords/admin/timereg/prosjekt/1714165BF0A709D4E0636402000AC62E/aktivitet/
Siste REST - individuell aktivitet
Denne koden definerer GET på en bestemt aktivitet:
ords.define_template(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid/aktivitet/:aktivitet_uid',
p_comments => 'Administrasjon av enkelt aktivitet til et prosjekt');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid/aktivitet/:aktivitet_uid',
p_method => 'GET',
p_source_type => ords.source_type_collection_item,
p_source => q'[
select p.navn prosjektnavn, a.navn aktivitetsnavn,
a.beskrivelse aktivitetsbeskrivelse,
rawtohex(p.guid) prosjekt_uid, rawtohex(a.guid) aktivitet_uid
from prosjekt p join aktivitet a on p.id = a.prosjekt_id
where p.guid=hextoraw(:uid)
and a.guid = hextoraw(:aktivitet_uid)]',
p_comments => 'Returnerer alle aktiviteter for prosjekt.');
Eksempel på GET mot en aktivitet:
curl http://rio:8080/ords/admin/timereg/prosjekt/1714165BF0A709D4E0636402000AC62E/aktivitet/1716D650145F0DF0E0636402000AE8A2
Det bør være en overkommelig oppgave å definere handlers for PUT og DELETE mot en enkeltaktivitet.
405 Method Not Allowed
Så langt har vi ikke definert hvordan PUT og DELETE mot en aktivitet skal håndteres; derfor får man HTTP 405 - Method Not Allowed når man forsøker seg på det. ORDS sjekker automatisk for hvilke operasjoner som er definert. Om en URL ikke kan håndteres i definerte handlers så vil man naturligvis få HTTP 404.
Hva er greia med UID?
Om du lurer på hvorfor UID brukes og ikke primærnøkkel (ID) så er det for å unngå scanning og masseuthenting via ID. ID stiger sekvensielt og om den var en del av URL i stedet for UID vil API-et bli mer sårbart for slikt misbruk.
Tips til slutt
Dersom du bruker verktøy som Postman så sjekk hvilke headere som sendes inn. Om det sendes inn User-Agent type nettleser vil ORDS gjerne svare med html når feil oppstår. Fra Curl som vist her returnerer den JSON også ved feil.
Det er mye funksjonalitet som ikke er dekket her. Blant annet sikkerhet. Vårt API er vid åpent. OAuth 2.0, OIDC et al er tema i neste post.
Sjekk dokumentasjon og søk etter tips fra blant annet Jeff Smith
Forhåpentligvis har denne gjennomgangen vist hvordan man gradvis kan bygge opp et REST API i en logisk struktur og tilpasses etter hvert.
REST API så langt
Komplett anonym PL/SQL-kode for REST API som er definert så langt, denne blokken er blitt så lang at den kunne vært splittet opp i en blokk pr template for eksempel:
begin
ords.enable_schema(p_url_mapping_pattern => 'admin');
ords.define_module(
p_module_name => 'timereg',
p_base_path => '/timereg/',
p_comments => 'REST API til timeregistrering'
);
ords.define_template(
p_module_name => 'timereg',
p_pattern => '/prosjekt/',
p_comments => 'Administrasjon av prosjekt');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/',
p_method => 'GET',
p_source_type => ords.source_type_collection_feed,
p_source => 'select navn, beskrivelse,starttid, avsluttet, rawtohex(guid) "uid" from prosjekt',
p_comments => 'Returnerer alle prosjekt.');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/',
p_method => 'POST',
p_source_type => ords.source_type_plsql,
p_source => q'[
begin
prosjekt_adm.nytt_prosjekt (
p_navn => :navn,
p_beskrivelse => :beskrivelse,
p_starttid => :starttid,
p_avsluttet => :avsluttet,
p_guid => :uid);
end;]',
p_comments => 'Oppretter et prosjekt.');
ords.define_parameter(
p_module_name => 'timereg',
p_pattern => '/prosjekt/',
p_method => 'POST',
p_name => 'uid' ,
p_bind_variable_name => 'uid',
p_source_type => 'RESPONSE',
p_param_type => 'STRING',
p_access_method => 'OUT',
p_comments =>'UID returned from database.');
ords.define_template(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid',
p_comments => 'Administrasjon av prosjekt');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid',
p_method => 'GET',
p_source_type => ords.source_type_collection_item,
p_source => q'[
select navn, beskrivelse,starttid, avsluttet, rawtohex(guid) "uid"
from prosjekt
where guid=hextoraw(:uid)]',
p_comments => 'Returnerer et prosjekt.');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid',
p_method => 'PUT',
p_source_type => ords.source_type_plsql,
p_source => q'[
begin
prosjekt_adm.oppdater_prosjekt(
p_guid => :uid,
p_navn => :navn,
p_beskrivelse => :beskrivelse,
p_starttid => :starttid,
p_avsluttet => :avsluttet);
end;]',
p_comments => 'Oppdaterer et prosjekt.');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid',
p_method => 'DELETE',
p_source_type => ords.source_type_plsql,
p_source => q'[
begin
prosjekt_adm.slett_prosjekt (
p_guid => :uid);
end;]',
p_comments => 'Sletter et prosjekt.');
ords.define_template(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid/aktivitet/',
p_comments => 'Administrasjon av aktiviteter til et prosjekt');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid/aktivitet/',
p_method => 'GET',
p_source_type => ords.source_type_collection_feed,
p_source => q'[
select p.navn prosjektnavn, a.navn aktivitetsnavn,
a.beskrivelse aktivitetsbeskrivelse,
rawtohex(p.guid) prosjekt_uid, rawtohex(a.guid) aktivitet_uid
from prosjekt p join aktivitet a on p.id = a.prosjekt_id
where p.guid=hextoraw(:uid)]',
p_comments => 'Returnerer alle aktiviteter for prosjekt.');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid/aktivitet/',
p_method => 'POST',
p_source_type => ords.source_type_plsql,
p_source => q'[
begin
aktivitet_adm.ny_aktivitet (
p_navn => :navn,
p_beskrivelse => :beskrivelse,
p_prosjekt_guid => :uid,
p_guid => :aktivitet_uid);
end;]',
p_comments => 'Oppretter en aktivitet for prosjekt.');
ords.define_parameter(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid/aktivitet/',
p_method => 'POST',
p_name => 'aktivitet_uid' ,
p_bind_variable_name => 'aktivitet_uid',
p_source_type => 'RESPONSE',
p_param_type => 'STRING',
p_access_method => 'OUT',
p_comments =>'UID til opprettet aktivitet.');
ords.define_template(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid/aktivitet/:aktivitet_uid',
p_comments => 'Administrasjon av enkelt aktivitet til et prosjekt');
ords.define_handler(
p_module_name => 'timereg',
p_pattern => '/prosjekt/:uid/aktivitet/:aktivitet_uid',
p_method => 'GET',
p_source_type => ords.source_type_collection_item,
p_source => q'[
select p.navn prosjektnavn, a.navn aktivitetsnavn,
a.beskrivelse aktivitetsbeskrivelse,
rawtohex(p.guid) prosjekt_uid, rawtohex(a.guid) aktivitet_uid
from prosjekt p join aktivitet a on p.id = a.prosjekt_id
where p.guid=hextoraw(:uid)
and a.guid = hextoraw(:aktivitet_uid)]',
p_comments => 'Returnerer alle aktiviteter for prosjekt.');
commit;
end;
/
Package bodies
Package body til rutinene som brukes fra ORDS vises under.
CREATE OR REPLACE PACKAGE BODY prosjekt_adm AS
FUNCTION nytt_prosjekt (
p_navn IN prosjekt.navn%TYPE,
p_beskrivelse IN prosjekt.beskrivelse%TYPE,
p_starttid IN prosjekt.starttid%TYPE DEFAULT sysdate,
p_avsluttet IN prosjekt.avsluttet%TYPE DEFAULT NULL
) RETURN prosjekt.id%TYPE AS
l_id prosjekt.id%TYPE;
BEGIN
INSERT INTO prosjekt (
navn,
beskrivelse,
starttid,
avsluttet
) VALUES (
p_navn,
p_beskrivelse,
nvl(p_starttid,sysdate),
p_avsluttet
) RETURNING id INTO l_id;
RETURN l_id;
END nytt_prosjekt;
PROCEDURE nytt_prosjekt (
p_navn IN prosjekt.navn%TYPE,
p_beskrivelse IN prosjekt.beskrivelse%TYPE,
p_starttid IN prosjekt.starttid%TYPE DEFAULT sysdate,
p_avsluttet IN prosjekt.avsluttet%TYPE DEFAULT NULL,
p_guid OUT VARCHAR2
) AS
BEGIN
INSERT INTO prosjekt (
navn,
beskrivelse,
starttid,
avsluttet
) VALUES (
p_navn,
p_beskrivelse,
nvl(p_starttid,sysdate),
p_avsluttet
) RETURNING rawtohex(guid) INTO p_guid;
END nytt_prosjekt;
PROCEDURE oppdater_prosjekt (
p_guid IN VARCHAR2,
p_navn IN prosjekt.navn%TYPE,
p_beskrivelse IN prosjekt.beskrivelse%TYPE,
p_starttid IN prosjekt.starttid%TYPE,
p_avsluttet IN prosjekt.avsluttet%TYPE DEFAULT NULL
) AS
BEGIN
UPDATE prosjekt
SET
navn = p_navn,
beskrivelse = p_beskrivelse,
avsluttet = p_avsluttet
WHERE
guid = hextoraw(p_guid);
END oppdater_prosjekt;
PROCEDURE slett_prosjekt (
p_guid IN VARCHAR2
) AS
BEGIN
DELETE FROM prosjekt
WHERE
guid = hextoraw(p_guid);
END slett_prosjekt;
END prosjekt_adm;
/
CREATE OR REPLACE PACKAGE BODY aktivitet_adm AS
PROCEDURE ny_aktivitet (
p_navn IN aktivitet.navn%TYPE,
p_beskrivelse IN aktivitet.beskrivelse%TYPE,
p_prosjekt_guid IN VARCHAR2,
p_guid OUT VARCHAR2
) AS
l_prosjekt_id prosjekt.id%TYPE;
BEGIN
SELECT
id
INTO l_prosjekt_id
FROM
prosjekt
WHERE
guid = hextoraw(p_prosjekt_guid);
INSERT INTO aktivitet (
navn,
beskrivelse,
prosjekt_id
) VALUES (
p_navn,
p_beskrivelse,
l_prosjekt_id
) RETURNING guid INTO p_guid;
END ny_aktivitet;
END aktivitet_adm;
/