UNPKG

reso-api-server

Version:
975 lines (887 loc) 32.6 kB
require('jaydata-reso'); var fs = require('fs') , randomstring = require("just.randomstring") , nonce = randomstring(16) , transportVersion = "0.9" , compression = require('compression') , connect = require('connect') , parse = require('url').parse , qs = require('qs') , http = require('http') , https = require('https') , auth = require('basic-auth') , url = require("url"); window.DOMParser = require('xmldom').DOMParser; // // functions shared between endpoints // function preProcessFn(req, res, next){ if (!req.query) { req.query = ~req.url.indexOf('?') ? qs.parse(parse(req.url).query) : {}; } next(); }; function bearerFn(config, parts, req, res, next){ var parsedURI = url.parse(config.oauth2ServerUrl); var post_options = { host: parsedURI.hostname, port: parsedURI.port, path: parsedURI.path, method: "GET", rejectUnauthorized: false, headers: { "Authorization": parts[0] + " " + parts[1], } }; var https_auth = require("https"); var post_req = https_auth.request(post_options, function(post_res) { post_res.setEncoding('utf8'); var msg = ""; post_res.on('data', function (chunk) { msg += chunk; }); post_res.on("end", function () { // // set the user in the request // msg = JSON.parse(msg); req.user = req.remoteUser = msg.name; // // move on // next(); }); }); post_req.end(); post_req.on('error', function(e) { console.trace(e); }); }; function basicAuthFn(config, req, res, next){ if (!config.basicAuth && !config.authServerUrl) { return next(); } var authorization = req.headers.authorization; if (!authorization) return unauthorizedBasic(res); // // Determine if Bearer authorization header is sent // var parts = authorization.split(" "); if (parts[0] == "Bearer") { return bearerFn(config, parts, req, res, next); } function unauthorizedBasic(res){ res.statusCode = 401; res.setHeader("WWW-Authenticate", 'Basic realm="' + config.authRealm + '"'); res.end("Unauthorized"); } var user = auth(req); if (user == null) { unauthorizedBasic(res); } else { if (typeof config.basicAuth == 'function'){ if (config.basicAuth(user.name, user.pass)) { req.user = req.remoteUser = user.name; next(); } else { unauthorizedBasic(res); } } else { var querystring = require("querystring"); var post_data = querystring.stringify({ "user_name" : user.name }); var parsedURI = url.parse(config.authServerUrl); var post_options = { host: parsedURI.hostname, port: parsedURI.port, path: parsedURI.path, method: "POST", rejectUnauthorized: false, headers: { "Content-Type": "application/x-www-form-urlencoded", "Content-Length": post_data.length } }; var https_auth = require("https"); var post_req = https_auth.request(post_options, function(post_res) { post_res.setEncoding('utf8'); var msg = ""; post_res.on('data', function (chunk) { msg += chunk; }); post_res.on("end", function () { msg = JSON.parse(msg); if (!msg) { unauthorizedBasic(res); } if (user.pass == msg.user_pass) { // // set the user in the request // req.user = req.remoteUser = user.name; next(); } else { unauthorizedBasic(res); } }); }); post_req.write(post_data); post_req.end(); post_req.on('error', function(e) { console.trace(e); }); } } }; function digestAuthFn(config, req, res, next){ if (!config.digestAuth && !config.authServerUrl) { return next(); } if (req.user) return next(); function unauthorizedDigest(res, realm) { res.statusCode = 401; res.setHeader("WWW-Authenticate", 'Digest realm="' + realm + '", nonce="' + nonce + '", qop=auth'); res.end("Unauthorized"); }; var authorization = req.headers.authorization; if (!authorization) return unauthorizedDigest(res, config.authRealm); // // Determine if Bearer authorization header is sent // var authparts = authorization.split(" "); if (authparts[0] !== "Digest") { if (authparts[0] == "Bearer") { return bearerFn(config, authparts, req, res, next); } else { return unauthorizedDigest(res, config.authRealm); } } // // determine if the correct number of arguments is in the Digest header // // var pos = authorization.indexOf(" "); // authorization = authorization.substring(pos); // var parts = authorization.split(","); var authorizationTokens = authorization.substring(authorization.indexOf(" ")); // var pos = authorization.indexOf(" "); // var authorizationTokens = authorization.substring(pos); var parts = authorizationTokens.split(","); if (parts.length !== 8) { return unauthorizedDigest(res, config.authRealm); } // // breakdown the header // var breakdown = new Array(); for (var i = parts.length; i--;) { // var pieces = parts[i].split("="); //console.dir(parts[i]); // breakdown[pieces[0].trim()] = pieces[1].replace(/["']/g, ""); var piece = parts[i].match(/^\s*?(\w+)="?([^"]*)"?\s*?$/); if (piece.length > 2) { breakdown[piece[1]] = piece[2]; } } function digestAuthResponse(breakdown, password, req, res, next) { // // construct a response // var crypto = require("crypto"); var HA1 = crypto.createHash("md5").update(breakdown.username + ":" + breakdown.realm + ":" + password, "utf8").digest("hex"); var HA2 = crypto.createHash("md5").update(req.method + ":" + breakdown.uri, "utf8").digest("hex"); var check_response = crypto.createHash("md5").update(HA1 + ":" + breakdown.nonce + ":" + breakdown.nc + ":" + breakdown.cnonce + ":" + breakdown.qop + ":" + HA2, "utf8").digest("hex"); // // check constructed response against the passed response // if (check_response != breakdown.response) { console.log("FAILED"); unauthorizedDigest(res, breakdown.realm); } // // set the user in the request // req.user = req.remoteUser = breakdown.username; next(); }; // // use the callback to determine the password to use // if (typeof config.digestAuth == 'function'){ var callback = config.digestAuth; digestAuthResponse(breakdown, callback(breakdown.username), req, res, next); } else { var querystring = require("querystring"); var post_data = querystring.stringify({ "user_name" : breakdown.username }); var parsedURI = url.parse(config.authServerUrl); var post_options = { host: parsedURI.hostname, port: parsedURI.port, path: parsedURI.path, method: "POST", rejectUnauthorized: false, headers: { "Content-Type": "application/x-www-form-urlencoded", "Content-Length": post_data.length } }; var https_auth = require("https"); var post_req = https_auth.request(post_options, function(post_res) { post_res.setEncoding('utf8'); var msg = ""; post_res.on('data', function (chunk) { msg += chunk; }); post_res.on("end", function () { msg = JSON.parse(msg); if (!msg) { unauthorizedDigest(res, breakdown.realm); } digestAuthResponse(breakdown, msg.user_pass, req, res, next); }); }); post_req.write(post_data); post_req.end(); post_req.on('error', function(e) { console.trace(e); }); } }; function errorFn(req, res, next, callback) { var domain = require("domain"); var reqd = domain.create(); reqd.add(req); reqd.add(res); reqd.add(next); reqd.on('error', function(err) { try { console.error(err); next(err); } catch (derr){ console.error("Error sending 500", derr, req.url); reqd.dispose(); } }); reqd.run(function(){ callback(); }); }; function errorHandlerFn(config, err, req, res, next) { if (config.errorHandler) { connect.errorHandler.title = typeof config.errorHandler == "string" ? config.errorHandler : config.provider.databaseName; connect.errorHandler()(err, req, res, next); } else { next(err); } }; // // resource endpoint // $data.ODataServer = function(config){ config.database = config.type.name; if (config.legacySourceUrl) { var parsedLegacyUrl = url.parse(config.legacySourceUrl); // protocol: parsedLegacyUrl.protocol, // host: parsedLegacyUrl.hostname, // port: parsedLegacyUrl.port parsedLegacyUrl.port, // path: parsedLegacyUrl.path, config.provider = { name: "libRETS", address: parsedLegacyUrl.protocol + "//" + parsedLegacyUrl.hostname, port: parsedLegacyUrl.port, user: "admin", password: "admin" }; } else { config.provider = { name: "mongoDB", databaseName: config.type.name, address: config.databaseAddress, port: config.databasePort, responseLimit: config.responseLimit || 100, user: config.user, checkPermission: config.checkPermission, externalIndex: config.externalIndex }; } var serviceType = $data.Class.defineEx(config.type.fullName + ".Service", [config.type, $data.ServiceBase]); serviceType.annotateFromVSDoc(); function postProcessFn(req, res){ if (config.postProcess) { if (typeof config.postProcess == 'function'){ function clearForProcessing(req) { if (!req.reso.memberName) { req.reso.memberName = "System"; } req.reso.requestor = req.connection.remoteAddress; if (req.reso.memberName == "$metadata") { req.reso.memberName = "Metadata"; } req.reso.endTime = new Date().getTime(); config.postProcess(req.method, req.reso); } // // server needs to complete its processing so results are delayed by 10 ms to allow thread to complete // var waits = 0; var waitTime = config.processWait; function timeout_wrapper(req) { return function() { ++waits; //console.log("Wait " + waits + " times for " + req.reso.startTime); if (req.reso.resultSize) { clearForProcessing(req); } else { if (waits > 2) { console.log("Shutdown Wait for " + req.reso.startTime); console.log("Consider increasing PROCESS_WAIT configuration value"); req.reso.resultSize = 1; req.reso.keyValue = "unknown"; clearForProcessing(req); } else { timeout = setTimeout(fn, waitTime); } } }; }; var fn = timeout_wrapper(req); var timeout = setTimeout(fn, waitTime); if (req.reso.resultSize) { clearTimeout(timeout); clearForProcessing(req); } } } }; /* var corsFn = function(req, res, next){ if (config.CORS !== false){ res.setHeader("Access-Control-Allow-Origin", req.headers.origin); res.setHeader("Access-Control-Allow-Headers", "X-Requested-With,X-HTTP-Method-Override,X-PINGOTHER,Content-Type,MaxDataServiceVersion,DataServiceVersion,Authorization,Accept"); res.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,PUT,MERGE,PATCH,DELETE"); if (req.method === "OPTIONS"){ res.setHeader("Access-Control-Allow-Credentials", "false"); res.setHeader("Access-Control-Max-Age", "31536000"); res.setHeader("Cache-Control", "max-age=31536000"); res.end(); }else{ next(); } }else next(); }; function simpleBodyFn(req, res, next) { $data.JayService.OData.Utils.simpleBodyReader()(req, res, next); }; */ return function(req, res, next){ var self = this; // // hide recursive calls that are denoted with an endpoint "/$batch" // var startStamp = new Date(); var endPointURL = unescape(req.url); if (config.provider.checkPermission){ Object.defineProperty(req, 'checkPermission', { value: config.provider.checkPermission.bind(req), enumerable: true }); config.provider.checkPermission = req.checkPermission; } if (req.method === 'OPTIONS') { console.log('!OPTIONS'); var headers = {}; // IE8 does not allow domains to be specified, just the * headers["Access-Control-Allow-Origin"] = req.headers.origin; headers["Access-Control-Allow-Methods"] = "HEAD,POST,GET,OPTIONS,PUT,MERGE,DELETE"; headers["Access-Control-Allow-Headers"] = "Host,Accept-Language,Accept-Encoding,Connection,User-Agent,Origin,Cache-Control,Pragma,X-Requested-With,X-HTTP-Method-Override,X-PINGOTHER,Content-Type,MaxDataServiceVersion,DataServiceVersion,Authorization,Accept"; headers["Access-Control-Allow-Credentials"] = "false"; // headers["Access-Control-Max-Age"] = '86400'; // 24 hours // headers["Access-Control-Max-Age"] = "31536000"; // headers["Cache-Control"] = "max-age=31536000"; headers["Access-Control-Max-Age"] = "1"; headers["Cache-Control"] = "max-age=1"; res.writeHead(200, headers); res.end(); } else { if (req.headers.origin) { res.setHeader("Access-Control-Allow-Origin", req.headers.origin); } res.setHeader("Access-Control-Allow-Headers", "Host,Accept-Language,Accept-Encoding,Referer,Connection,User-Agent,Origin,Cache-Control,Pragma,x-requested-with,X-HTTP-Method-Override,X-PINGOTHER,Content-Type,MaxDataServiceVersion,DataServiceVersion,Authorization,Accept"); res.setHeader("Access-Control-Allow-Methods", "HEAD,POST,GET,OPTIONS,PUT,MERGE,DELETE"); res.setHeader('Access-Control-Allow-Credentials', "false"); // res.setHeader("Access-Control-Max-Age", "31536000"); // res.setHeader("Cache-Control", "max-age=31536000"); res.setHeader("Access-Control-Max-Age", "1"); res.setHeader("Cache-Control", "max-age=1"); function processFn(req, res, next) { config.provider.user = config.user = req.user || req.remoteUser || config.user || config.provider.user || "anonymous"; if (endPointURL != "/$batch") { if (config.logEntry) { console.log(startStamp + " " + "Request " + req.method + " " + endPointURL + " by " + config.provider.user + " received from " + req.connection.remoteAddress); } } preProcessFn(req, res, function(){ // simpleBodyFn(req, res, function(){ $data.JayService.OData.Utils.simpleBodyReader()(req, res, function() { errorFn(req, res, next, function(){ req.reso = { "externalIndex" : config.externalIndex, "startTime": startStamp.getTime(), "userName" : config.provider.user } //console.dir(serviceType); //junk = new serviceType(config.provider); //console.dir(junk); $data.JayService.createAdapter( serviceType, function() { return new serviceType(config.provider); } ).call( self, req, res, function(err) { if (typeof err === "string") { err = new Error(err); } errorHandlerFn(err, req, res, next); } ); postProcessFn(req, res); }); // errorFn }); // simpleBodyFn }); // preProcessFn } switch(config.authType) { case "Basic": basicAuthFn(config, req, res, function(){ processFn(req, res, next); }); // basicAuthFn break case "Digest": digestAuthFn(config, req, res, function(){ processFn(req, res, next); }); // digestAuthFn break default: processFn(req, res, next); } // switch authType } // return } // not method OPTIONS }; // // DataService (discovery) endpoint // $data.DataServiceServer = function(config, dataServiceEndpoint, resourceEndpoint, resourceList, metadata){ return function(req, res, next){ var self = this; var startStamp = new Date(); function processFn(req, res, next) { config.provider.user = config.user = req.user || req.remoteUser || config.user || config.provider.user || "anonymous"; if (config.logEntry) { console.log(startStamp + " " + "Discovery Request by " + config.provider.user + " received from " + req.connection.remoteAddress); } preProcessFn(req, res, function(){ function dataServiceMetadata(req, res, next) { function generateResourceHeader() { return "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + "<feed xml:base=\"" + dataServiceEndpoint + "/\" " + "xmlns=\"http://www.w3.org/2005/Atom\" " + "xmlns:d=\"http://schemas.microsoft.com/ado/2007/08/dataservices\" " + "xmlns:m=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\" " + "xmlns:georss=\"http://www.georss.org/georss\" " + "xmlns:gml=\"http://www.opengis.net/gml\">\r\n" + " <id>" + dataServiceEndpoint + "</id>\r\n" + " <title type=\"text\">DataSystem</title>\r\n" + " <updated>" + metadata.lastUpdate.toISOString() + "</updated>\r\n" + " <link rel=\"self\" title=\"DataSystem\" href=\"DataSystem\" />\r\n" + " <entry>\r\n" + " <id>" + dataServiceEndpoint + "/DataSystem('" + config.serverName + "')</id>\r\n" + " <category term=\"RESO.OData.Transport.DataSystem\" scheme=\"http://schemas.microsoft.com/ado/2007/08/dataservices/scheme\" />\r\n" + " <link rel=\"edit\" title=\"DataSystem\" href=\"DataSystem('" + config.serverName + "')\" />\r\n" + " <title>Data Services for " + config.serverName + "</title>\r\n" + " <updated>" + metadata.lastUpdate.toISOString() + "</updated>\r\n" + " <author>\r\n" + " <name>" + metadata.author + "</name>\r\n" + " </author>\r\n" + " <content type=\"application/xml\">\r\n" + " <m:properties>\r\n" + " <d:Name>" + config.serverName + "</d:Name>\r\n" + " <d:ServiceURI>" + dataServiceEndpoint + "</d:ServiceURI>\r\n" + " <d:DateTimeStamp m:type=\"Edm.DateTime\">" + (new Date()).toISOString() + "</d:DateTimeStamp>\r\n" + " <d:TransportVersion>" + transportVersion + "</d:TransportVersion>\r\n" + " <d:Resources m:type=\"Collection(RESO.OData.Transport.Resource)\">\r\n"; }; function generateResourceElement(aName, aDate) { return " <d:element>\r\n" + " <d:Name>" + aName + "</d:Name>\r\n" + " <d:ServiceURI>" + resourceEndpoint + "</d:ServiceURI>\r\n" + " <d:Description>RESO Standard " + aName + " Resource</d:Description>\r\n" + " <d:DateTimeStamp m:type=\"Edm.DateTime\">" + aDate.toISOString() + "</d:DateTimeStamp>\r\n" + " <d:TimeZoneOffset m:type=\"Edm.Int32\">" + (0 - (aDate.getTimezoneOffset()/60)) + "</d:TimeZoneOffset>\r\n" + " <d:Localizations m:type=\"Collection(RESO.OData.Transport.Localization)\" />\r\n" + " </d:element>\r\n"; } function generateResourceFooter() { return " </d:Resources>\r\n" + " <d:ID>" + config.serverName + "</d:ID>\r\n" + " </m:properties>\r\n" + " </content>\r\n" + " </entry>\r\n" + "</feed>"; }; var aDoc = generateResourceHeader(); for (eName in resourceList) { aDoc += generateResourceElement(eName, resourceList[eName]); } aDoc += generateResourceFooter(); res.statusCode = 200; res.setHeader("content-type", "application/xml;charset=UTF-8"); res.setHeader("content-length", Buffer.byteLength(aDoc)); res.end(aDoc); }; errorFn(req, res, next, function(){ var aQuery = decodeURIComponent(req._parsedUrl.query); switch (aQuery) { case "DataSystem": dataServiceMetadata(req, res, next); break; case "DataSystem('" + config.serverName + "')": dataServiceMetadata(req, res, next); break; default: res.statusCode = 500; errorHandlerFn(config, "Unknown DataService Query " + aQuery, req, res, next); } }); // errorFn }); // preProcessFn } switch(config.authType) { case "Basic": basicAuthFn(config, req, res, function(){ processFn(req, res, next); }); break case "Digest": digestAuthFn(config,req, res, function(){ processFn(req, res, next); }); break default: config.provider.user = config.user = req.user || req.remoteUser || config.user || config.provider.user || "anonymous"; processFnx(req, res, next); } // switch authType }; // return }; // // http service // $data.createODataServer = function(config) { var bannerWidth = 78; var bannerText; function bannerTop() { bannerText = "."; for (var i = bannerWidth; i--;) { bannerText += "-"; } bannerText += "."; console.log(bannerText); } function bannerSpacer() { bannerText = "|"; for (var i = bannerWidth; i--;) { bannerText += "-"; } bannerText += "|"; console.log(bannerText); } function bannerLine(text) { if (!text) { text = ""; } bannerText = "| " + text; for (var i = (bannerWidth-text.length-1); i--;) { bannerText += " "; } bannerText += "|"; console.log(bannerText); } function bannerBottom() { bannerText = "'"; for (var i = bannerWidth; i--;) { bannerText += "-"; } bannerText += "'"; console.log(bannerText); } // // general configuration items // var projectName = "RESO API Server"; config.serverName = config.serverName || projectName; bannerTop(); var packageName = projectName + " Version " + require('./package').version; if (config.serverName == projectName ) { bannerLine(packageName); } else { bannerLine(config.serverName); bannerLine("(" + packageName + ")"); } bannerSpacer(); switch(config.authType) { case "Basic": config.authRealm = config.authRealm || config.serverName; bannerLine("- Supports " + config.authType + " Authentication"); break; case "Digest": config.authRealm = config.authRealm || config.serverName; bannerLine("- Supports " + config.authType + " Authentication"); bannerLine(" > Digest realm: " + config.authRealm); bannerLine(" > Digest nonce: " + nonce); break; default: bannerLine("- No Authentication is being used"); }; if (config.authServerUrl) { config.basicAuth = null; config.digestAuth = null; bannerLine("- Accounts are maintained externally and accessed using:"); bannerLine(" > " + config.authServerUrl); } else { bannerLine("- Account information is maintained within the server"); }; if (config.oauth2ServerUrl) { bannerLine("- Requests validated with OAuth2 Server using:"); bannerLine(" > " + config.oauth2ServerUrl); } if (config.compression) { bannerLine("- Output will ALWAYS be compressed if the requestor supports compression"); } else { bannerLine("- Output will NEVER be compressed"); }; bannerBottom(); // // indexing // config.databaseAddress = "127.0.0.1"; config.databasePort = 27017; var Db = require("mongodb").Db , Server = require("mongodb").Server; var db = new Db(config.type.name, new Server(config.databaseAddress, config.databasePort), { safe : false } ); db.open(function(err, db) { if (err) throw err; // // construct an arrays of collection information: // - collections not using guid as a key // - creationDate by collection // var definitions = config.type.memberDefinitions; var resourceDate = new Date(); var indexList = {}; var resourceList = {}; for (dName in definitions) { if (definitions[dName] !== undefined) { if (definitions[dName].kind == "property") { if (definitions[dName].type.fullName == "$data.EntitySet") { resourceList[dName.substring(1)] = resourceDate; var entitySet = definitions[dName]; var keyProperties = entitySet.elementType.memberDefinitions.getKeyProperties(); if (keyProperties.length > 0) { if (keyProperties[0].originalType != "id") { indexList[dName.substring(1)] = keyProperties[0].name; } } } } } } function startup() { db.close(); // // transport // var getIPAddress = function() { var interfaces = require('os').networkInterfaces(); for (var devName in interfaces) { var iface = interfaces[devName]; for (var i = iface.length; i--;) { var alias = iface[i]; if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) { return alias.address; } } } return '0.0.0.0'; } var serverHost = config.host || getIPAddress() || "localhost"; var serverPort = config.port || 80; var serverProtocol = config.protocol || "http"; var app; app = connect(); var serverPath = config.path || "/"; if (config.compression) { app.use(compression()); } // // Resource Endpoint handler // app.use(serverPath, $data.ODataServer(config)); // // DataSystem Endpoint handler // var dataSystemPath = "/DataSystem.svc"; var resourceEndpoint = serverProtocol + "://" + serverHost + ":" + serverPort + serverPath; var dataServiceEndpoint = serverProtocol + "://" + serverHost + ":" + serverPort + dataSystemPath; var metadata = { author: config.serverName || "unknown", version: "unknown" }; if (!config.metadata) { var pjson = JSON.parse(fs.readFileSync("./node_modules/reso-data-dictionary/package.json", "utf8")); metadata.author = pjson.author.name; metadata.version = pjson.version; metadata.lastUpdate= fs.statSync("./node_modules/reso-data-dictionary/index.js").mtime; } else { metadata.lastUpdate = fs.statSync(config.metadata).mtime; } app.use(dataSystemPath, $data.DataServiceServer(config, dataServiceEndpoint, resourceEndpoint, resourceList, metadata)); if (serverProtocol == "http" ) { http.createServer(app).listen(serverPort, serverHost); } else { https.createServer(config.certificates, app).listen(serverPort, serverHost); } // // console notification // bannerLine(); bannerLine("Listening on " + serverProtocol + "://" + serverHost + ":" + serverPort + serverPath); bannerBottom(); bannerTop(); bannerLine("DataService Endpoint"); bannerSpacer(); bannerLine("Listening on " + serverProtocol + "://" + serverHost + ":" + serverPort + dataSystemPath); bannerBottom(); } bannerTop(); bannerLine("Resource Endpoint"); bannerSpacer(); // // use a RETS server through libRETS otherwise use a local MongoDb repository // if (config.legacySourceUrl) { bannerLine("- RESO Certified RETS Server contains listings"); bannerLine(" > URL: " + config.legacySourceUrl); bannerLine("- Using libRETS for communications"); startup(); } else { bannerLine("- Local repository requires indexing"); // // no need to apply special indexing if all collections use guid // function isEmptyObject(obj) { for(var prop in obj) { if (Object.prototype.hasOwnProperty.call(obj, prop)) { return false; } } return true; } if (isEmptyObject(indexList)) { bannerLine("- No special indexing is required"); startup(); } // // process collections that don't use default guid as key // for (cName in indexList) { var cObject = {}; var cKey = indexList[cName]; cObject[cKey] = 1; bannerLine("- Indexing " + cName); var collection = db.collection(cName); // // external index feature needs conflicting index removed and a scan of keys // if (config.externalIndex) { collection.indexInformation({full:true}, function(err, indexInformation) { var dropList = []; for (var i = indexInformation.length; i--;) { var anIndex = indexInformation[i]; if (anIndex.unique) { //console.dir(anIndex); var found = false; for (cIndexKey in anIndex.key) { if (cIndexKey == cKey) { found = true; } } if (found) { dropList[dropList.length] = anIndex.key; } } } function scanAndCreate(aDefinition) { collection.find({},aDefinition).toArray(function(err, docs) { //console.dir(docs); if (err) throw err; INDEX = []; for (var j = docs.length; j--;) { INDEX[j] = docs[j].ListingId; } bannerLine(" > An in-memory index for " + cName + " has been created with " + docs.length + " items"); if (i < 0) { startup(); } }); }; if (dropList.length == 0) { scanAndCreate(cObject); } else { for (var i = dropList.length; i--;) { db.dropIndex(cName, dropList[i], function(err, result) { if (!err) { bannerLine(" > Conflicting built-in index was dropped"); } }); scanAndCreate(dropList[i]); } // dropList loop } // dropList.length == 0 }); // collection.indexInformation } else { // // builtin-in index must be present // bannerLine(" > Uniqueness enforced with built-in index"); collection.ensureIndex(cObject, {unique:true, background:true, dropDups:true, w:0}, function(err, indexName) { if (err) throw err; if (!indexName) { bannerLine(" > Built-in index for " + cName + " was not found and was automatically created"); } // Fetch full index information collection.indexInformation({full:true}, function(err, indexInformation) { // db.indexInformation(cName, {full:true}, function(err, indexInformation) { for (var i = indexInformation.length; i--;) { if (indexInformation[i].name != "_id_") { bannerLine(" > Built-in index name for " + cName + " is " + indexInformation[i].name); } } startup(); }); }); // collection.ensureIndex } // if config.externalIndex } // if local repopsitory } // indexDriver }); // db.open }; module.exports = exports = $data.ODataServer; function readIndexFile(fileName) { if (fs.existsSync(fileName)) { var contents = fs.readFileSync(fileName).toString(); return contents.split(","); } return new Array(); }