Files
Facilitor/APPL/API/api_SOAP.asp
Jos Groot Lipman 20b683fadb FCLT#88774 Opruimen oude upgrade-code
svn path=/Website/trunk/; revision=69051
2025-05-19 10:09:08 +00:00

372 lines
15 KiB
Plaintext

<%@ language = "JavaScript" %>
<% /*
$Revision$
$Id$
File: api_SOAP.asp
Description: API om een SOAP-achtige service te bieden gebaseerd op een bestaande view.
Dat is informatie in XML op te leveren (of een ander formaat dat je via een XSL
kunt bewerkstelligen) uit een view en bepaalde toegestande filters.
Wat de API kan en doet (view, filters, limiet) moet in fac_api gedefinieerd zijn,
geidentificeerd door de apiname.
Parameters: apiname, apikey
Context: Door een remote systeem (geen persoon) om info uit FACILITOR te halen aan te roepen
Notes: De readrechten van de user die met de meegegeven apikey wordt geidentificeerd
gehanteerd worden. TODO!
Het is Oracle die in eerste instantie de XML fabriceert.
BELANGRIJK:
De bedoeling is dat de DE M2M SOAP/Machine manier is om info uit FACILITOR te krijgen.
De enige manier.
Dit mechanisme is dus zo generiek mogelijk, en er worden zoveel mogelijk conventies gehanteerd
Iedere aanpassing of gewenste customisation moet kunnen ZONDER dit mechanisme aan te passen en
ZONDER een ander mechanisme te gebruiken. Om een dergelijke service te kunnen gebruiken moet:
- een view bestaan (gebruik bij voorkeur een bestaande voorgedefinieerde MOD_V_API_XXX view)
- een definitierecord (API) in fac_api bestaan/worden toegevoegd die per request aan die views linkt
- een user (met APIKEY) de juiste rechten krijgen
Vervolgens kan de service worden aangeroepen met API en APIKEY, en bevat de geposte XML
de criteria. Een resulterende XML met een Response is het resultaat, alsmede een HTTP-statuscode
waarbij 200 Ok is.
Status: Autorisatie tegen de API_KEY-user checken
*/
DOCTYPE_Disable = 1;
LCL_Disable = 1
THIS_FILE = "appl/api/api_soap.asp";
// __Logging = 1;
%>
<!-- #include file="../Shared/common.inc" -->
<!-- #include file="../Shared/xml_converter.inc" -->
<!-- #include file="../api/api.inc" -->
<%
Session.Codepage = 65001;
Response.Charset = 'utf-8';
Session.Abandon(); // Voor de zekerheid
try
{
var API = new API_func(); // Valideert ook
var asJSON = getQParam("json","0")!="0";
var parsed = RequestXML(API);
if (parsed.error)
{
__DoLog("Error " + parsed.error);
}
var xmlReq = parsed.xml;
if (xmlReq.documentElement.firstChild.prefix)
var soapns = xmlReq.documentElement.firstChild.prefix + ":";
else
var soapns = "";
var body = xmlReq.getElementsByTagName(soapns+"Body");
if (!body.length) {
throw("Invalid request: No XML body");
}
var GetReq = body[0].firstChild.baseName;
// req heeft vorm GetXXX, met XXX is de req van de definitie in fac_api
var req = GetReq.substring(3);
var requestData = null;
// de apidata.viewmapping is een JSON string met (voorbeeld)
// {"req": "Reserveringen",
// "node": "reservering",
// "view": "res_v_api_reserveringen",
// "filter":{"locatiecode":"locatiecode", "catalogus": "catalogus", "begindatumtijd": {"colName": "van", "operand":"GT", "datatype": "date"}, "einddatumtijd": {"colName": "tot", "operand":"LT", "datatype": "date"}},
// "limit":75}
for (i=0; i < API.apidata.viewmapping.length; i++)
{
if (API.apidata.viewmapping[i].req == req)
{
requestData = API.apidata.viewmapping[i];
}
}
if (requestData == null) {
API.error("Invalid request: " + GetReq);
}
var viewname = requestData.view;
var resnodename = requestData.node;
var autfunction = requestData.autfunction; // optioneel
if (!autfunction)
{
autfunction =
{"regio": "WEB_ALGUSE",
"district": "WEB_ALGUSE",
"locatie": "WEB_ALGUSE",
"gebouw": "WEB_ALGUSE",
"verdieping": "WEB_ALGUSE",
"ruimte": "WEB_ALGUSE",
"bestelling": "WEB_BESUSE",
"bestelopdracht": "WEB_BESUSE",
"afspraak": "WEB_BEZUSE",
"contract": "WEB_CNTUSE",
"factuur": "WEB_EXTFIN",
"deel": "WEB_INSUSE",
"melding": "WEB_MLDUSE",
"opdracht": "WEB_MLDUSE",
"afdeling": "WEB_PRSUSE",
"persoon": "WEB_PRSUSE",
"reservering": "WEB_RESUSE",
"voorziening": "WEB_RESUSE"}[resnodename];
}
// Nu filters zoeken
var wheres = [];
var filters = body[0].firstChild.childNodes;
for (i=0; i < filters.length; i++)
{
var filterName = filters[i].baseName;
var found = false;
for (xx in requestData.filter) // kennen we dit filter?
{
if (filterName == xx)
{
var found = true;
var wildtext = filters[i].text;
var colHash = requestData.filter[xx];
if (typeof colHash == "string") // backwards compatible
{
colHash = { colName: colHash,
datatype: "string",
operand : "LIKE"
};
wildtext += "%";
}
else
{
// heeft colHash de volgende structuur
/*
res_datum: { colName: "res_rsv_ruimte_van",
datatype: "date",
operand: "LT"
}
*/
}
var criterium = safe.quoted_sql(filters[i].text);
switch(colHash.datatype)
{
case "string" :
criterium = safe.quoted_sql_upper(filters[i].text);
break;
case "date" :
// Er zijn regels over datums
// http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/datatypes.html#isoformats
// We willen natuurlijk dat het datatype in de view gewoon datum is
// en dan moeten we van de string van het criterium volgens conventie een date maken
// TODO: Oracle vindt dat standaard Date niet DateTime is...
// Voor filtering is dit perfect zo, maar de presentatie in de XML moet ..?
// TOCH TODO: wel of geen seconden??!?
criterium = "TO_DATE("+safe.quoted_sql(filters[i].text)+", 'yyyy-mm-dd\"T\"hh24:mi:ss')";
break;
}
switch(colHash.operand)
{
case "EQ":
wheres.push("UPPER("+colHash.colName + ") = " + criterium)
break;
case "LIKE":
// altijd case-insensitive vind ik oke, maar de wildcard zou IMO in het filter moeten worden meegegeven.
wheres.push("UPPER("+colHash.colName + ") LIKE " + safe.quoted_sql_wild(wildtext))
break;
case "LT":
wheres.push(""+colHash.colName + " < " + criterium)
break;
case "GT":
wheres.push(""+colHash.colName + " > " + criterium)
break;
case "LTE":
wheres.push(""+colHash.colName + " <= " + criterium)
break;
case "GTE":
wheres.push(""+colHash.colName + " >= " + criterium)
break;
}
}
}
if (!found && (filterName.match(/\_KEY$/i) || filterName.toUpperCase() == "KEY") // fallback: op _KEY velden mag je automatisch filteren met EQ
&& parseInt(filters[i].text, 10))
{
var sql = "SELECT data_type"
+ " , data_length"
+ " , data_precision"
+ " , data_scale"
+ " FROM user_tab_columns"
+ " WHERE table_name = " + safe.quoted_sql_upper(viewname)
+ " AND column_name = " + safe.quoted_sql_upper(filterName);
var oRs = Oracle.Execute(sql);
if (!oRs.Eof)
{
wheres.push(filterName + " = " + parseInt(filters[i].text, 10))
}
oRs.Close();
}
}
// Bu: 3D scope toepassen
// Check for our special 3D column names
var has3D = {};
var any3D = false;
sql = "SELECT UPPER(column_name) column_name"
+ " FROM user_tab_columns"
+ " WHERE table_name = " + safe.quoted_sql_upper(viewname)
+ " AND UPPER(column_name) LIKE 'FCLT_3D_%'";
var oRs = Oracle.Execute(sql);
while (!oRs.eof)
{
any3D = true;
has3D[oRs("column_name").Value] = true;
oRs.MoveNext();
}
oRs.Close();
if (any3D)
{
if (autfunction)
var authparams = user.checkAutorisation(autfunction);
// TODO: Onderstaande code gaan delen met fac_usrrap.inc?
if (has3D["FCLT_3D_DISCIPLINE_KEY"])
{
var disc_3d = " FCLT_3D_DISCIPLINE_KEY IN"
+ " (SELECT ins_discipline_key FROM fac_v_webgebruiker "
+ " WHERE fac_functie_key = " + authparams.autfunctionkey
+ " AND prs_perslid_key = " + user_key + ")";
if (has3D["FCLT_3D_DISCIPLINE2_KEY"])
disc_3d = " (" + disc_3d + " OR "
+ " FCLT_3D_DISCIPLINE2_KEY IN"
+ " (SELECT ins_discipline_key FROM fac_v_webgebruiker "
+ " WHERE fac_functie_key = " + authparams.autfunctionkey
+ " AND prs_perslid_key = " + user_key + ")"
+ ")";
wheres.push(disc_3d);
}
if (has3D["FCLT_3D_LOCATIE_KEY"] && authparams.ALGreadlevel > -1)
{
wheres.push(" FCLT_3D_LOCATIE_KEY IN"
+ " (SELECT alg_locatie_key FROM fac_v_my_locations "
+ " WHERE niveau ="+authparams.ALGreadlevel
+ " AND prs_perslid_key="+user_key+")");
}
if (has3D["FCLT_3D_GEBOUW_KEY"] && authparams.ALGreadlevel > -1)
{
wheres.push(" FCLT_3D_GEBOUW_KEY IN"
+ " (SELECT alg_gebouw_key FROM fac_v_my_buildings "
+ " WHERE niveau ="+authparams.ALGreadlevel
+ " AND prs_perslid_key="+user_key+")");
}
if (has3D["FCLT_3D_AFDELING_KEY"] && authparams.PRSreadlevel > 0)
{
wheres.push(" FCLT_3D_AFDELING_KEY IN"
+ " (SELECT prs_afdeling_key FROM prs_v_afdeling_familie a"
+ " WHERE a.prs_afdeling_elder_key IN"
+ " (SELECT aa.prs_afdeling_elder_key"
+ " FROM prs_v_afdeling_familie aa"
+ " WHERE aa.prs_afdeling_key = " + user.prs_afdeling_key()
+ " AND aa.niveau = " + authparams.PRSreadlevel + "))");
}
if (has3D["FCLT_3D_USER_KEY"] )
{
wheres.push(" FCLT_3D_USER_KEY = " + user_key);
}
}
if (requestData.limit)
wheres.push("ROWNUM <= " + requestData.limit);
var where="";
if (wheres.length)
where = "WHERE " + wheres.join(" AND ");
var resultnode = GetReq+'Response'; // Dit is volgens onze eigen conventie zo
var stylesheet = API.apidata.stylesheet;
var key = -1;
var xtrakey = -1;
var sql_params = safe.quoted_sql(viewname) + ", "
+ key + " , "
+ safe.quoted_sql(customerId) + ", "
+ "'$AspSession$', "
+ xtrakey + ", "
+ safe.quoted_sql(where)
+ ", " + safe.quoted_sql(resultnode)
+ ", " + safe.quoted_sql(resnodename);
var sql = "SELECT xml.make_view_xml2(" + sql_params + ") xml_blob FROM dual";
var oRs = Oracle.Execute(sql);
var xml_content = oRs("xml_blob").Value;
oRs.Close();
__Log("XML blob is {0} karakters.".format(xml_content.length));
if (API.apidata.loglevel) __Log2File(xml_content, API.APIname + "_DATA");
if (asJSON)
Response.ContentType = "application/json";
else
Response.ContentType = "text/xml";
Response.AddHeader("Access-Control-Allow-Origin", "*"); // Opdat FireFox cross-domain toestaat
// Als er geen afwijkende xsl is gedefinieerd dan leveren we gewoon generiek de rapport_data node
// op van het resultaat,dus maar zonder de metadata, dus alleen de facilitor/[resultnode]/
if (stylesheet == null)
{
var xmldoc = new ActiveXObject("Msxml2.DOMDocument.6.0");
xmldoc.async = false;
xmldoc.loadXML(xml_content);
if (xmldoc.parseError.errorCode != 0)
{
var myErr = xmldoc.parseError;
throw("Error in xmlfile: " + myErr.reason);
}
else
{
var resultdoc = xmldoc.getElementsByTagName(resultnode)[0];
if (resultdoc)
{
if (asJSON)
{
var antwoord = JSON.stringify(xmlToJson(resultdoc),null, getQParam("pretty","0")=="1"?2:0);
}
else
var antwoord = resultdoc.xml;
var oCrypto = new ActiveXObject("SLNKDWF.Crypto");
var eTag = '"' + oCrypto.hex_sha1(antwoord).toLowerCase() + '"';
Response.AddHeader("ETag", eTag);
if (Request.ServerVariables("HTTP_IF_NONE_MATCH") == eTag)
{ // We hebben een match!
Response.Clear();
Response.Status = "304 Not modified";
Response.End;
}
if (API.apidata.loglevel) __Log2File(antwoord, API.APIname + "_OUT");
Response.write(antwoord);
}
}
}
else
{
// die req-parameter is waarschijnlijk vrij zinloos
var params = { mode: req };
if (API.apidata.loglevel)
params.log_postfix = API.APIname + "_OUT";
STR2Stream(xml_content, stylesheet, Response, params);
}
}
catch (e)
{
API.error(typeof e == "string"? e : e.description);
}
ASPPAGE_END();
%>