UNPKG

foxhound

Version:

A Database Query generation library.

480 lines (428 loc) 12.8 kB
/** * FoxHound Meadow Endpoints Dialect * * @license MIT * * @author Steven Velozo <steven@velozo.com> * @class FoxHoundDialectMeadowEndpoints */ var FoxHoundDialectMeadowEndpoints = function() { /** * Generate a table name from the scope * * @method: generateTableName * @param: {Object} pParameters SQL Query Parameters * @return: {String} Returns the table name clause */ var generateTableName = function(pParameters) { return pParameters.scope; }; /** * Generate the Identity column from the schema or scope * * @method: generateIdentityColumnName * @param: {Object} pParameters SQL Query Parameters * @return: {String} Returns the table name clause */ var generateIdentityColumnName = function(pParameters) { // TODO: See about using the Schema or the Schemata for this return `ID${pParameters.scope}`; }; /** * Generate a field list from the array of dataElements * * Each entry in the dataElements is a simple string * * @method: generateFieldList * @param: {Object} pParameters SQL Query Parameters * @return: {String} Returns the field list clause */ var generateFieldList = function(pParameters) { var tmpDataElements = pParameters.dataElements; if (!Array.isArray(tmpDataElements) || tmpDataElements.length < 1) { return ''; } var tmpFieldList = ''; for (var i = 0; i < tmpDataElements.length; i++) { if (i > 0) { tmpFieldList += ','; } tmpFieldList += tmpDataElements[i]; } return tmpFieldList; }; /** * Generate a query from the array of where clauses * * Each clause is an object like: { Column:'Name', Operator:'EQ', Value:'John', Connector:'And', Parameter:'Name' } * * @method: generateWhere * @param: {Object} pParameters SQL Query Parameters * @return: {String} Returns the WHERE clause prefixed with WHERE, or an empty string if unnecessary */ var generateWhere = function(pParameters) { var tmpFilter = Array.isArray(pParameters.filter) ? pParameters.filter : []; var tmpTableName = generateTableName(pParameters); var tmpURL = ''; let tmpfAddFilter = (pFilterCommand, pFilterParameters) => { if (tmpURL.length > 0) { tmpURL += '~'; } tmpURL += `${pFilterCommand}~${pFilterParameters[0]}~${pFilterParameters[1]}~${pFilterParameters[2]}`; }; let tmpfTranslateOperator = (pOperator) => { tmpNewOperator = 'EQ'; switch(pOperator.toUpperCase()) { case '!=': tmpNewOperator = 'NE'; break; case '>': tmpNewOperator = 'GT'; break; case '>=': tmpNewOperator = 'GE'; break; case '<=': tmpNewOperator = 'LE'; break; case '<': tmpNewOperator = 'LT'; break; case 'LIKE': tmpNewOperator = 'LK'; break; case 'IN': tmpNewOperator = 'INN'; break; case 'NOT IN': tmpNewOperator = 'NI'; break; } return tmpNewOperator; } // Translating Delete Tracking bit on query to a query with automagic // This will eventually deprecate this as part of the necessary query if (pParameters.query.disableDeleteTracking) { tmpfAddFilter('FBV',['Deleted','GE','0']) } for (var i = 0; i < tmpFilter.length; i++) { if (tmpFilter[i].Operator === '(') { tmpfAddFilter('FOP',['0','(','0']); } else if (tmpFilter[i].Operator === ')') { // Close a logical grouping tmpfAddFilter('FCP',['0',')','0']); } else if (tmpFilter[i].Operator === 'IN' || tmpFilter[i].Operator === "NOT IN") { let tmpFilterCommand = 'FBV'; if (tmpFilter[i].Connector == 'OR') { tmpFilterCommand = 'FBVOR'; } // Add the column name, operator and parameter name to the list of where value parenthetical tmpfAddFilter(tmpFilterCommand, [tmpFilter[i].Column, tmpfTranslateOperator(tmpFilter[i].Operator), tmpFilter[i].Value.map(encodeURIComponent).join(',')]) } else if (tmpFilter[i].Operator === 'IS NULL') { // IS NULL is a special operator which doesn't require a value, or parameter tmpfAddFilter('FBV', [tmpFilter[i].Column, 'IN', '0']); } else if (tmpFilter[i].Operator === 'IS NOT NULL') { // IS NOT NULL is a special operator which doesn't require a value, or parameter tmpfAddFilter('FBV', [tmpFilter[i].Column, 'NN', '0']); } else { let tmpFilterCommand = 'FBV'; if (tmpFilter[i].Connector == 'OR') { tmpFilterCommand = 'FBVOR'; } // Add the column name, operator and parameter name to the list of where value parenthetical tmpfAddFilter(tmpFilterCommand, [tmpFilter[i].Column, tmpfTranslateOperator(tmpFilter[i].Operator), encodeURIComponent(tmpFilter[i].Value)]); } } let tmpOrderBy = generateOrderBy(pParameters); if (tmpOrderBy) { if (tmpURL) { tmpURL += '~'; } tmpURL += tmpOrderBy; } return tmpURL; }; /** * Get the flags for the request * * These are usually passed in for Update and Create when extra tracking is disabled. * * @method: generateFlags * @param: {Object} pParameters SQL Query Parameters * @return: {String} Flags to be sent, if any. */ function generateFlags(pParameters) { let tmpDisableAutoDateStamp = pParameters.query.disableAutoDateStamp; let tmpDisableDeleteTracking = pParameters.query.disableDeleteTracking; let tmpDisableAutoIdentity = pParameters.query.disableAutoIdentity; let tmpDisableAutoUserStamp = pParameters.query.disableAutoUserStamp; let tmpFlags = ''; let fAddFlag = (pFlagSet, pFlag) => { if (pFlagSet) { if (tmpFlags.length > 0) { tmpFlags += ','; } tmpFlags += pFlag; } }; fAddFlag(tmpDisableAutoDateStamp, 'DisableAutoDateStamp'); fAddFlag(tmpDisableDeleteTracking, 'DisableDeleteTracking'); fAddFlag(tmpDisableAutoIdentity, 'DisableAutoIdentity'); fAddFlag(tmpDisableAutoUserStamp, 'DisableAutoUserStamp'); return tmpFlags; }; /** * Get the ID for the record, to be used in URIs * * @method: getIDRecord * @param: {Object} pParameters SQL Query Parameters * @return: {String} ID of the record in string form for the URI */ var getIDRecord = function(pParameters) { var tmpFilter = Array.isArray(pParameters.filter) ? pParameters.filter : []; var tmpIDRecord = false; if (tmpFilter.length < 1) { return tmpIDRecord; } for (var i = 0; i < tmpFilter.length; i++) { // Check Schema Entry Type var tmpSchema = Array.isArray(pParameters.query.schema) ? pParameters.query.schema : []; var tmpSchemaEntry = {Column:tmpFilter[i].Column, Type:'Default'}; for (var j = 0; j < tmpSchema.length; j++) { // If this column is the AutoIdentity, set it. if ((tmpFilter[i].Column == tmpSchema[j].Column) && (tmpSchema[j].Type == 'AutoIdentity')) { tmpIDRecord = tmpFilter[i].Value; break; } } } return tmpIDRecord; } /** * Generate an ORDER BY clause from the sort array * * Each entry in the sort is an object like: * {Column:'Color',Direction:'Descending'} * * @method: generateOrderBy * @param: {Object} pParameters SQL Query Parameters * @return: {String} Returns the field list clause */ var generateOrderBy = function(pParameters) { var tmpOrderBy = pParameters.sort; var tmpOrderClause = false; if (!Array.isArray(tmpOrderBy) || tmpOrderBy.length < 1) { return tmpOrderClause; } tmpOrderClause = ''; for (var i = 0; i < tmpOrderBy.length; i++) { if (i > 0) { tmpOrderClause += '~'; } tmpOrderClause += `FSF~${tmpOrderBy[i].Column}~`; if (tmpOrderBy[i].Direction == 'Descending') { tmpOrderClause += 'DESC~0'; } else { tmpOrderClause += 'ASC~0' } } return tmpOrderClause; }; /** * Generate the limit clause * * @method: generateLimit * @param: {Object} pParameters SQL Query Parameters * @return: {String} Returns the table name clause */ var generateLimit = function(pParameters) { if (!pParameters.cap) { return ''; } let tmpBegin = (pParameters.begin !== false) ? pParameters.begin : 0; return `${tmpBegin}/${pParameters.cap}`; }; var Create = function(pParameters) { var tmpTableName = generateTableName(pParameters); var tmpFlags = generateFlags(pParameters); if (tmpTableName) { let tmpURL = tmpTableName; if (tmpFlags) { tmpURL = `${tmpURL}/WithFlags/${tmpFlags}` } return tmpURL; } else { return false; } }; /** * Read one or many records * * @method Read * @param {Object} pParameters SQL Query parameters * @return {String} Returns the current Query for chaining. */ var Read = function(pParameters) { var tmpTableName = generateTableName(pParameters); var tmpFieldList = generateFieldList(pParameters); var tmpWhere = generateWhere(pParameters); var tmpLimit = generateLimit(pParameters); var tmpURL = `${tmpTableName}`; // In the case that there is only a single query parameter, and the parameter is a single identity, // we will cast it to the READ endpoint rather than READS. if ((pParameters.filter) && (pParameters.filter.length == 1) // If there is exactly one query filter parameter && (pParameters.filter[0].Column === generateIdentityColumnName(pParameters)) // AND It is the Identity column && (pParameters.filter[0].Operator === '=') // AND The comparators is a simple equals && (tmpLimit == '') && (tmpFieldList == '') // AND There is no limit or field list set && (!pParameters.sort)) // AND There is no sort clause { // THEN This is a SINGLE READ by presumption. // There are some bad side affects this could cause with chaining and overridden behaviors, if // we are requesting a filtered list of 1 record. tmpURL = `${tmpURL}/${pParameters.filter[0].Value}`; } else { tmpURL = `${tmpURL}s`; if (tmpFieldList) { tmpURL = `${tmpURL}/LiteExtended/${tmpFieldList}` } if (tmpWhere) { tmpURL = `${tmpURL}/FilteredTo/${tmpWhere}`; } if (tmpLimit) { tmpURL = `${tmpURL}/${tmpLimit}`; } } return tmpURL; }; var Update = function(pParameters) { var tmpTableName = generateTableName(pParameters); var tmpFlags = generateFlags(pParameters); if (tmpTableName) { let tmpURL = tmpTableName; if (tmpFlags) { tmpURL = `${tmpURL}/WithFlags/${tmpFlags}` } return tmpURL; } else { return false; } }; var Delete = function(pParameters) { var tmpTableName = generateTableName(pParameters); var tmpIDRecord = getIDRecord(pParameters); if (!tmpIDRecord) { return false; } return `${tmpTableName}/${tmpIDRecord}`; }; var Count = function(pParameters) { var tmpTableName = generateTableName(pParameters); var tmpWhere = generateWhere(pParameters); let tmpCountQuery = `${tmpTableName}s/Count`; if (tmpWhere) { return `${tmpTableName}s/Count/FilteredTo/${tmpWhere}`; } return tmpCountQuery; }; var tmpDialect = ({ Create: Create, Read: Read, Update: Update, Delete: Delete, Count: Count }); /** * Dialect Name * * @property name * @type string */ Object.defineProperty(tmpDialect, 'name', { get: function() { return 'MeadowEndpoints'; }, enumerable: true }); return tmpDialect; }; module.exports = FoxHoundDialectMeadowEndpoints;