368 lines
15 KiB
Plaintext
368 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";
|
|
|
|
// Session("logging")=1;
|
|
%>
|
|
<!-- #include file="../Shared/common.inc" -->
|
|
<!-- #include file="../Shared/xml_converter.inc" -->
|
|
<!-- #include file="../Shared/json2.js" -->
|
|
<!-- #include file="../api/api.inc" -->
|
|
<%
|
|
Session.Codepage = 65001;
|
|
Response.Charset = 'utf-8';
|
|
Session.Abandon(); // Voor de zekerheid
|
|
|
|
try
|
|
{
|
|
var APIname = getQParam("API");
|
|
var APIKEY = getQParam("APIKEY");
|
|
var API = new API_func(APIname, APIKEY); // Valideert ook
|
|
|
|
var asJSON = getQParam("json","0")!="0";
|
|
|
|
var xmlReq = Server.CreateObject("MSXML2.DOMDocument.6.0");
|
|
xmlReq.load(Request);
|
|
|
|
if (API.apidata.loglevel) __Log2File(xmlReq.xml, APIname + "_IN");
|
|
|
|
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_FINUSE",
|
|
"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)
|
|
API.error("3d yet autfunction undefined"); // Strict genomen niet nodig voor FCLT_3D_USER_KEY
|
|
|
|
var authparams = user.checkAutorisation(autfunction);
|
|
// TODO: Onderstaande code gaan delen met fac_usrrap.inc?
|
|
if (has3D["FCLT_3D_DISCIPLINE_KEY"])
|
|
{
|
|
wheres.push(" 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_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;
|
|
sql = "BEGIN xml.make_view_xml("
|
|
+ safe.quoted_sql(viewname) + ", "
|
|
+ key + " , "
|
|
+ safe.quoted_sql(customerId) + ", "
|
|
+ safe.quoted_sql(Session.SessionId) + ", "
|
|
+ xtrakey + ", "
|
|
+ safe.quoted_sql(where)
|
|
+ ", " + safe.quoted_sql(resultnode)
|
|
+ ", " + safe.quoted_sql(resnodename)
|
|
+ "); END;";
|
|
Oracle.Execute(sql);
|
|
sql = "SELECT fac_xml_xml FROM fac_xml WHERE fac_session_id = " + safe.quoted_sql(Session.SessionId) + " ORDER BY fac_xml_volgnr";
|
|
oRs = Oracle.Execute( sql);
|
|
var xml_content = "";
|
|
while (!oRs.eof)
|
|
{
|
|
xml_content = xml_content + oRs(0).value;
|
|
oRs.moveNext();
|
|
}
|
|
__Log("XML is " + xml_content.length + " karakters");
|
|
|
|
if (API.apidata.loglevel) __Log2File(xml_content, 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, APIname + "_OUT");
|
|
Response.write(antwoord);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// die req-parameter is waarschijnlijk vrij zinloos
|
|
var params = { mode: req };
|
|
if (API.apidata.loglevel)
|
|
params.log_postfix = APIname + "_OUT";
|
|
STR2Stream(xml_content, stylesheet, Response, params);
|
|
}
|
|
}
|
|
catch (e)
|
|
{
|
|
API.error(typeof e == "string"? e : e.description);
|
|
}
|
|
%> |