API 2.0 in wording

svn path=/Website/trunk/; revision=20879
This commit is contained in:
Jos Groot Lipman
2014-03-06 10:56:03 +00:00
parent ab70765204
commit 3094e02bbc
10 changed files with 727 additions and 1 deletions

381
APPL/API2/api2.inc Normal file
View File

@@ -0,0 +1,381 @@
<% /*
$Revision$
$Id$
File: api.inc
Description: Functies voor API's
Notes:
*/
%>
<!-- #include file="../Shared/save2db.inc" -->
<%
Session.Abandon(); // Altijd, voor de zekerheid
api2 = {
authenticate: function _authenticate()
{
var APIKEY;
if (S("fac_api_key_in_url"))
APIKEY = getQParam("APIKEY", "");
if (!APIKEY)
APIKEY = String(Request.ServerVariables("HTTP_X_FACILITOR_API_KEY")); // Meegegeven als X-FACILITOR-API-Key
var sql = "SELECT prs_perslid_key, prs_perslid_naam"
+ " FROM prs_perslid"
+ " WHERE prs_perslid_apikey = " + safe.quoted_sql(APIKEY);
var oRs = Oracle.Execute(sql);
if (oRs.Eof || !APIKEY)
{
Response.Status = "401 Unauthorized";
//Response.AddHeader("WWW-Authenticate", "Basic realm=\"FACILITOR API\"");
//shared.simpel_page("");
Response.End;
};
/* global */ user_key = oRs("prs_perslid_key").Value;
/* global */ user = new Perslid(user_key);
__Log("API2 User is: " + oRs("prs_perslid_naam").Value);
oRs.Close();
},
process: function _process(model)
{
api2.authenticate();
// Voor PUT en POST:
// var params = RequestJSON();
// if (!params)
// API.error("Error parsing input JSON");
var method = Request.ServerVariables("REQUEST_METHOD");
if (!("REST_" + method in model))
{
Response.Status = "405 Method not allowed";
// TODO The response MUST include an Allow header containing a list of valid methods for the requested resource.
Response.End;
}
var data = model["REST_" + method]( { filter: api2.qs2json(), include: getQParamArray("include",[]) } );
api2.deliver(data, getQParamSafe("format", "json"),
model.records_name,
model.record_name,
(method != "GET")||getQParamInt("key","0")>0); // PUT en POST altijd single
},
qs2json: function _qs2json(params)
{
var filter = {};
for (var i = 1; i<= Request.QueryString.Count; i++)
{
filter[Request.QueryString.key(i)] = String(Request.QueryString(i));
}
return filter;
},
// Verwerk filtervelden diei in de url zijn meegegeven
sqlfilter: function _sqlfilter(params, model)
{
var wheres = [];
if (params.filter)
{
for (var fld in model.fields)
{
var field = model.fields[fld];
var filter = field.filter;
if (!filter)
continue;
if (field.name in params.filter)
{
var filterval = params.filter[field.name];
// TODO: operand?
var clause;
switch (field.typ)
{
case "key":
var safe_val = parseInt(filterval, 10);
break;
case "varchar":
var safe_val = safe.quoted_sql(filterval);
break;
}
wheres.push(field.dbs + " = " + safe_val);
}
}
}
return wheres;
},
sqlfields: function _sqlfields(params, model)
{
var selects = [];
var tables = [ model.table ];
var wheres = [];
for (var fld in model.fields)
{
var field = model.fields[fld];
selects.push(model.table + "." + field.dbs);
if (field.foreign)
{
var foreign = foreignKeyTable(field.foreign);
if (!foreign)
MISSING_FOREIGN;
var sqlf = "SELECT " + foreign.desc
+ " FROM " + foreign.tbl + " " + (foreign.alias||"xx")
+ " WHERE ";
if (foreign.where)
sqlf += foreign.where + " AND ";
sqlf += (foreign.alias||"xx") + "." + foreign.key + " = " + model.table + "." + field.dbs;
selects.push("(" + sqlf + ") name_" + field.dbs);
}
}
if (params.include && model.includes)
{
for (var i in params.include)
{
if (params.include[i] in model.includes)
{
var inc = model.includes[params.include[i]];
var incmodel = inc.model;
var incquery = api2.sqlfields(params, incmodel)
selects = selects.concat (incquery.selects);
tables = tables.concat (incquery.tables);
wheres = wheres.concat (incquery.wheres);
wheres.push ( model.table + "." + model.primary + "=" + incmodel.table + "." + inc.joinfield);
}
}
}
return { selects: selects, tables: tables, wheres: wheres };
},
sql2jsonval: function(oRs, field)
{
var val = oRs(field.dbs).Value;
if (field.typ == "date" && (val != null))
val = new Date(val)
if (field.typ == "datetime" && (val != null))
val = new Date(val)
// TODO: Wat te doen met lege waarde
// action: null
// action: {key: null, name: null}
// action: {}
// of helemaal weglaten? We hebben nu de 1e optie
if (val && field.foreign)
{
var name = oRs("name_" + field.dbs).Value;
if (name != null && typeof name == "date" )
name = new Date(name);
val = { key: val, name: name };
}
return val;
},
sql2jsonfields: function (oRs, model)
{
var record = {};
for (var fld in model.fields)
record[model.fields[fld].name] = api2.sql2jsonval(oRs, model.fields[fld]);
return record;
},
sql2json: function _sql2json(params, sql, model)
{
var data = [];
var oRs = Oracle.Execute(sql);
var lastkey = -1;
while (!oRs.Eof)
{
var key = oRs(model.primary).Value;
if (key != lastkey)
{
if (lastkey > 0)
data.push(record);
var record = {};
}
var fld;
for (fld in model.fields)
record[model.fields[fld].name] = api2.sql2jsonval(oRs, model.fields[fld]);
if (params.include && model.includes)
{
for (var i in params.include)
{
var incname = params.include[i];
if (incname in model.includes)
{
var incmodel = model.includes[incname].model;
if (!(incname in record))
record[incname] = [];
var increcord = {};
for (fld in incmodel.fields)
increcord[incmodel.fields[fld].name] = api2.sql2jsonval(oRs, incmodel.fields[fld]);
record[incname].push(api2.sql2jsonfields(oRs, incmodel));
}
}
}
lastkey = key;
oRs.MoveNext();
}
if (lastkey > 0)
data.push(record);
return data;
},
// Data is een array met 'records'
deliver: function _deliver(data, format, records_name, record_name, single )
{
Session.Codepage = 65001; // We doen *uitsluitend* utf-8
Response.Charset = 'utf-8';
if (single && !data.length)
{
Response.Status = "404 Not Found";
Response.End;
}
switch (format)
{
case "json":
var result = { };
if (single)
result[record_name] = data[0];
else
result[records_name] = data;
var str_antwoord = JSON.stringify(result, null, getQParam("pretty","0")=="1"?2:0);
var jsonp = getQParam("jsonp", getQParam("callback",""));
if (jsonp)
{
str_antwoord = jsonp + "(" + str_antwoord + ")";
Response.ContentType = "application/javascript";
}
else
Response.ContentType = "application/json";
break;
case "html":
Response.ContentType = "text/html";
var result = { };
if (single)
result[record_name] = data[0];
else
result[records_name] = data;
var antwoord = JSON.stringify(result, null, 2);
var str_antwoord = "<!DOCTYPE html><html><head></head><body><pre>"
+ Server.HTMLEncode(antwoord)
+ "</pre></body></html>";
break;
case "xml":
Response.ContentType = "text/xml";
var xml_antwoord = api2.json2xml(data, records_name, record_name, single);
// TODO: Output XSL transform ondersteunen?
var str_antwoord = xml_antwoord.xml;
break;
default:
WRONG_FORMAT;
}
// str_antwoord heeft nu het te versturen antwoord
// Bepaal eTag
var oCrypto = new ActiveXObject("SLNKDWF.Crypto");
var eTag = '"' + oCrypto.hex_sha1(String(S("cache_changecounter")) + "_" + str_antwoord).toLowerCase() + '"';
Response.AddHeader("ETag", eTag);
if (Request.ServerVariables("HTTP_IF_NONE_MATCH") == eTag)
{ // We hebben een match! Effectief besparen wel alleen op dataverkeer, de queries zijn al geweest
Response.Clear();
Response.Status = "304 Not modified";
Response.End;
}
// if (API.apidata.loglevel) __Log2File(str_antwoord, APIname + "_OUT");
Response.write(str_antwoord);
},
// TODO: Wanneer attributes gebruiken en wanneer (sub)-elements?
json2xml: function _json2xml(data, rootname, record_name, single)
{
var xmlDoc = new ActiveXObject("MSXML2.DOMDocument.4.0");
xmlDoc.appendChild(xmlDoc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\""));
var record2json = function(record, record_name)
{
var elementRecord = xmlDoc.createElement(record_name);
for (var fld in record)
{
var elementField = xmlDoc.createElement(fld);
if (record[fld] instanceof Date)
{
var elementFieldText = xmlDoc.createTextNode(String(record[fld].toJSON()));
elementField.appendChild(elementFieldText);
}
else if (record[fld] instanceof Array)
{
for (var i = 0; i < record[fld].length; i++)
elementField.appendChild(record2json(record[fld][i], "visitor")); // TODO Hardcoded
}
else if (record[fld] && typeof record[fld] == "object") // TODO: veronderstelt nog hardcoded dat dit foreign met name/key is
{
elementField.setAttribute("name", record[fld].name);
elementField.setAttribute("key", record[fld].key);
}
else
{
var elementFieldText = xmlDoc.createTextNode(record[fld]||"");
elementField.appendChild(elementFieldText);
}
elementRecord.appendChild(elementField);
}
return elementRecord;
};
if (single)
{
xmlDoc.appendChild(record2json(data[0], record_name));
}
else
{
var arrayElement = xmlDoc.createElement(rootname);
for (var i = 0; i < data.length; i++)
arrayElement.appendChild(record2json(data[i], record_name));
xmlDoc.appendChild(arrayElement);
}
return xmlDoc;
}
}
// LET OP: Verwacht wordt dat de JSON-code in de body utf-8 encoded is, niet windows-1252!
// (in de praktijk moet je *moeite* doen om windows-1252 te krijgen dus dit is handiger)
function RequestJSON()
{
var jvraag;
if(Request.TotalBytes > 0)
{
var lngBytesCount = Request.TotalBytes;
jvraag = BytesToStr(Request.BinaryRead(lngBytesCount));
}
__Log("Vraag: " + jvraag);
try
{
var vraag = myJSON.parse(jvraag);
}
catch (e)
{
__DoLog("eval faalt met: {0}<br>{1}".format(e.description, jvraag), "ffff00");
return null;
}
return vraag;
}
function BytesToStr(bytes)
{
var stream = Server.CreateObject("ADODB.Stream");
stream.type = 1;
stream.open;
stream.write(bytes);
stream.position = 0;
stream.type = 2; // Text
stream.charset = "utf-8";
var sOut = stream.readtext();
stream.close;
return sOut;
}
%>

View File

@@ -0,0 +1,27 @@
<%@ language = "JavaScript" %>
<% /*
$Revision$
$Id$
File: api_appointments.asp
Description: BEZ_AFSPRAAK API
Parameters:
Context: Door een remote systeem (geen persoon) om info uit FACILITOR te halen aan te roepen
Notes:
*/
DOCTYPE_Disable = true;
ANONYMOUS_Allowed = 1; // Eigenlijk niet waar. We regelen echter alles zelf
THIS_FILE = "appl/api/api_appointments.asp";
// Session("logging")=1;
%>
<!-- #include file="../Shared/common.inc" -->
<!-- #include file="./api2.inc" -->
<!-- #include file="../Shared/json2.js" -->
<!-- #include file="./model_appointments.inc" -->
<%
api2.process(model_appointments);
%>

View File

@@ -0,0 +1,27 @@
<%@ language = "JavaScript" %>
<% /*
$Revision$
$Id$
File: api_buildings.asp
Description: ALG_GEBOUW API
Parameters:
Context: Door een remote systeem (geen persoon) om info uit FACILITOR te halen aan te roepen
Notes:
*/
DOCTYPE_Disable = true;
ANONYMOUS_Allowed = 1; // Eigenlijk niet waar. We regelen echter alles zelf
THIS_FILE = "appl/api/api_buildings.asp";
// Session("logging")=1;
%>
<!-- #include file="../Shared/common.inc" -->
<!-- #include file="./api2.inc" -->
<!-- #include file="../Shared/json2.js" -->
<!-- #include file="./model_buildings.inc" -->
<%
api2.process(model_buildings);
%>

View File

@@ -0,0 +1,27 @@
<%@ language = "JavaScript" %>
<% /*
$Revision$
$Id$
File: api_visitors.asp
Description: BEZ_AFSPRAAK API
Parameters:
Context: Door een remote systeem (geen persoon) om info uit FACILITOR te halen aan te roepen
Notes:
*/
DOCTYPE_Disable = true;
ANONYMOUS_Allowed = 1; // Eigenlijk niet waar. We regelen echter alles zelf
THIS_FILE = "appl/api/api_visitors.asp";
// Session("logging")=1;
%>
<!-- #include file="../Shared/common.inc" -->
<!-- #include file="./api2.inc" -->
<!-- #include file="../Shared/json2.js" -->
<!-- #include file="./model_visitors.inc" -->
<%
api2.process(model_visitors);
%>

View File

@@ -0,0 +1,94 @@
<% /*
$Revision$
$Id$
File: model_appointments.inc
Description: Afspraken model. Dit bestand heeft niets met interfacing te maken
maar werkt uitsluitend op JSON-data
Parameters:
Context:
Notes:
*/
%>
<!-- #include file="../Shared/discxalg3d.inc"-->
<!-- #include file="./model_visitors.inc" -->
<%
model_appointments =
{
table: "bez_afspraak",
primary: "bez_afspraak_key",
records_name: "appointments",
record_name: "appointment",
fields: [{ name: "key", dbs: "bez_afspraak_key", typ: "key", filter: "exact" },
{ name: "from", dbs: "bez_afspraak_datum", typ: "datetime"},
{ name: "to", dbs: "bez_afspraak_eind", typ: "datetime"},
{ name: "description", dbs: "bez_afspraak_opmerking", typ: "varchar"},
{ name: "action", dbs: "bez_actie_key", typ: "key", foreign: "bez_actie"}
//{ name: "location", dbs: "alg_locatie_key", typ: "key", foreign: "alg_locatie"},
],
includes: {"visitors": { model: model_visitors,
joinfield: "bez_afspraak_key"
}
},
REST_GET: function _GET(params)
{
var urole = "fe"; // TODO: Moet echt niet ter zake doen
var autfunction = urole == "fe"? "WEB_ALGUSE" : "WEB_ALGMAN";
params.authparams = user.checkAutorisation(autfunction, null, null, true); // pessimistisch
// TODO: Add authorization hoewel je eigen afspraken zien natuurlijk niet echt spannend is
var query = api2.sqlfields(params, model_appointments );
query.wheres.push("(bez_afspraak_contact_key = " + user_key // Altijd fe vooralnog
+ " OR bez_afspraak_host_key = " + user_key + ")");
query.wheres.push("bez_afspraak_datum BETWEEN SYSDATE - " + S("facilitiespast_bez") + " AND SYSDATE + " + S("facilitiesfuture_bez"));
var wheres = api2.sqlfilter(params, model_appointments);
query.wheres = query.wheres.concat(wheres);
var sql = "SELECT " + query.selects.join(", ")
+ " FROM " + query.tables.join(", ")
+ " WHERE " + query.wheres.join(" AND " )
+ " ORDER BY bez_afspraak_datum, bez_afspraak.bez_afspraak_key";
__DoLog(sql);
var json = api2.sql2json (params, sql, model_appointments );
return json;
},
REST_PUT: function (params) /* update appointment */
{
// TODO: De key niet uit de data halen maar uit de url!
var data = RequestJSON();
var fields = []; // Build inserter
for (fld in model_appointments.fields)
{
var field = model_appointments.fields[fld];
if (field.name != "key" && field.name in data.appointment)
{
var newval = data.appointment[field.name];
if (field.typ == "key" && typeof newval == "object")
newval = newval.key;
var newfield = { dbs: field.dbs,
typ: field.typ,
val: newval };
fields.push(newfield);
}
}
// bezUpd heeft na afloop eventueel oldjsvals
var bezUpd = buildTrackingUpdate("bez_afspraak", " bez_afspraak_key = " + data.appointment["key"], fields, { noValidateToken: true });
__DoLog(bezUpd.sql);
Oracle.Execute(bezUpd.sql);
return [ data.appointment ]; // Ongewijzigd terug?
// TODO Beter een (re)get op de database zodat we *alle* velden teruggeven
},
POST: function (params) /* new appointment */
{
},
DELETE: function (params) /* delete appointment */
{
}
}
%>

View File

@@ -0,0 +1,62 @@
<% /*
$Revision$
$Id$
File: model_buildings.inc
Description: Gebouwen model. Dit bestand heeft niets met interfacing te maken
maar werkt uitsluitend op JSON-data
Parameters:
Context:
Notes:
*/
%>
<!-- #include file="../Shared/discxalg3d.inc"-->
<%
model_buildings =
{
table: "alg_gebouw",
primary: "alg_gebouw_key",
records_name: "buildings",
record_name: "building",
fields: [{ name: "key", dbs: "alg_gebouw_key", typ: "key", filter: "exact" },
{ name: "code", dbs: "alg_gebouw_code", typ: "varchar", filter: "like" },
{ name: "name", dbs: "alg_gebouw_naam", typ: "varchar", filter: "like" },
{ name: "description", dbs: "alg_gebouw_omschrijving", typ: "varchar"},
{ name: "location", dbs: "alg_locatie_key", typ: "key", foreign: "alg_locatie"},
],
REST_GET: function _GET(params)
{
var urole = "fe"; // TODO: Moet echt niet ter zake doen
var autfunction = urole == "fe"? "WEB_ALGUSE" : "WEB_ALGMAN";
params.authparams = user.checkAutorisation(autfunction, null, null, true); // pessimistisch
// TODO: Add authorization
var query = api2.sqlfields(params, model_buildings);
query.wheres.push("alg_gebouw_verwijder IS NULL");
var wheres = api2.sqlfilter(params, model_buildings)
query.wheres = query.wheres.concat(wheres);
var sql = "SELECT " + query.selects.join(", ")
+ " FROM " + query.tables.join(", ")
+ " WHERE " + query.wheres.join(" AND " )
+ " ORDER BY alg_gebouw_code";
var json = api2.sql2json (params, sql, model_buildings);
return json;
},
PUT: function (params) /* update building */
{
},
POST: function (params) /* new building */
{
},
DELETE: function (params) /* delete building */
{
}
}
%>

View File

@@ -0,0 +1,72 @@
<% /*
$Revision$
$Id$
File: model_visitors.inc
Description: Bezoekers model. Dit bestand heeft niets met interfacing te maken
maar werkt uitsluitend op JSON-data
Parameters:
Context:
Notes:
*/
%>
<!-- #include file="../Shared/discxalg3d.inc"-->
<%
model_visitors =
{
table: "bez_bezoekers",
primary: "bez_bezoekers_key",
records_name: "visitors",
record_name: "visitor",
fields: [{ name: "key", dbs: "bez_bezoekers_key", typ: "key", filter: "exact" },
{ name: "name", dbs: "bez_afspraak_naam", typ: "varchar"},
{ name: "company", dbs: "bez_afspraak_bedrijf", typ: "varchar"},
{ name: "badge", dbs: "bez_bezoekers_pasnr", typ: "varchar"},
{ name: "in", dbs: "bez_bezoekers_done", typ: "date"},
{ name: "out", dbs: "bez_bezoekers_out", typ: "date"}
// niet teruglinken, { name: "appointment", dbs: "bez_afspraak_key", typ: "key", foreign: "bez_afspraak"}
],
REST_GET: function _GET(params)
{
var urole = "fe"; // TODO: Moet echt niet ter zake doen
var autfunction = urole == "fe"? "WEB_ALGUSE" : "WEB_ALGMAN";
params.authparams = user.checkAutorisation(autfunction, null, null, true); // pessimistisch
// TODO: Add authorization
var query = api2.sqlfields(params, model_visitors );
query.tables.push("bez_afspraak");
query.wheres.push("(bez_afspraak_contact_key = " + user_key // Altijd fe vooralnog
+ " OR bez_afspraak_host_key = " + user_key + ")");
query.wheres.push("bez_afspraak.bez_afspraak_key = bez_bezoekers.bez_afspraak_key");
query.wheres.push("bez_afspraak_datum BETWEEN SYSDATE - " + S("facilitiespast_bez") + " AND SYSDATE + " + S("facilitiesfuture_bez"));
var wheres = api2.sqlfilter(params, model_visitors);
query.wheres = query.wheres.concat(wheres);
var sql = "SELECT " + query.selects.join(", ")
+ " FROM " + query.tables.join(", ")
+ " WHERE " + query.wheres.join(" AND " )
+ " ORDER BY bez_afspraak_datum, bez_afspraak.bez_afspraak_key";
__DoLog(sql);
var json = api2.sql2json (params, sql, model_visitors );
return json;
},
PUT: function (params) /* update building */
{
},
POST: function (params) /* new building */
{
},
DELETE: function (params) /* delete building */
{
}
}
%>

24
APPL/FAC/fac_iirf.ini Normal file
View File

@@ -0,0 +1,24 @@
# IsapiRewrite4.ini
#
# ini file for the ISAPI rewriter.
#
#
# $Id$
#
# RewriteLog c:\temp\iirf2
# RewriteLogLevel 0
RewriteHeader FCLT-REWRITER ^$ 1.00
RewriteBase ON
# /api2/buildings/1234.xml?apikey=apikey123 /default.asp?api2=buildings&format=xml&key=1234&apikey=apikey123
RewriteRule ^/api2/([a-z]+)/(\d+)\.(xml|json|html)/? /default.asp?api2=$1&format=$3&key=$2 [QSA]
# /api2/buildings.xml?apikey=apikey123 /default.asp?api2=buildings&format=xml&apikey=apikey123
RewriteRule ^/api2/([a-z]+)\.(xml|json|html)/? /default.asp?api2=$1&format=$2 [QSA]
# /melding/1234 /appl/mld/mld_melding.asp?mld_key=1234
# Geeft relatieve pad problemen
# RewriteRule ^/melding/(\d+) /appl/mld/mld_melding.asp?mld_key=$1 [QSA]

View File

@@ -148,7 +148,7 @@ if (Application("otap_environment") == "O" || Session("logging") > 0)
var loghtml = "";
if (logfilename)
{
loghtml = " of de <a href='../../" + Server.HTMLEncode(logfilename) + ".html?" + (new Date).getTime() + "' target='_blank'>gewone logfile </a>";
loghtml = " of de <a href='" + rooturl + "/" + Server.HTMLEncode(logfilename) + ".html?" + (new Date).getTime() + "' target='_blank'>gewone logfile </a>";
}
Response.Write("\n<hr><p>Zie <a href='" + Server.HTMLEncode(logurl) + "' target='_blank'>AiAi logfile</a> " + loghtml + "voor meer details of klik <a href='javascript:location.reload()'>Refresh</a> om het opnieuw te proberen.</p>");

12
IIRF.ini Normal file
View File

@@ -0,0 +1,12 @@
# IsapiRewrite4.ini
#
# ini file for the ISAPI rewriter.
#
# Geschikt voor Ionic's Isapi Rewrite Filter
# Deze kan optioneel(!) op de FACILITOR webserver ingericht worden
# zodat mooiere url's mogelijk worden.
#
# $Id$
#
IncludeIni appl\fac\fac_iirf.ini