Skip to content

Commit

Permalink
Refactor addRecord to recordBuilder (StackExchange#157) (StackExchang…
Browse files Browse the repository at this point in the history
…e#163)

* helpers.js: Refactor addRecord to recordBuilder

* Fixed failing dns test

* helpers.js: Fixed a typo in SRV handling

* helpers.js: Move type to top level argument

* Removed support for numeric modifiers

* helpers.js: Added IMPORT_TRANSFORM ttl argument back
  • Loading branch information
whs authored and Craig Peterson committed Aug 11, 2017
1 parent ba3eb07 commit ab70c36
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 189 deletions.
326 changes: 198 additions & 128 deletions pkg/js/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,14 @@ function DefaultTTL(v) {
}
}

function makeCAAFlag(value){
return function(record){
record.caaflag |= value;
};
}

// CAA_CRITICAL: Critical CAA flag
var CAA_CRITICAL = 1<<0;
var CAA_CRITICAL = makeCAAFlag(1<<0);


// DnsProvider("providerName", 0)
Expand All @@ -135,84 +141,71 @@ function DnsProvider(name, nsCount){
}

// A(name,ip, recordModifiers...)
function A(name, ip) {
var mods = getModifiers(arguments,2)
return function(d) {
addRecord(d,"A",name,ip,mods)
}
}
var A = recordBuilder('A');

// AAAA(name,ip, recordModifiers...)
function AAAA(name, ip) {
var mods = getModifiers(arguments,2)
return function(d) {
addRecord(d,"AAAA",name,ip,mods)
}
}
var AAAA = recordBuilder('AAAA');

// ALIAS(name,target, recordModifiers...)
function ALIAS(name, target) {
var mods = getModifiers(arguments,2)
return function(d) {
addRecord(d,"ALIAS",name,target,mods)
}
}
var ALIAS = recordBuilder('ALIAS');

// CAA(name,tag,value, recordModifiers...)
function CAA(name, tag, value){
checkArgs([_.isString, _.isString, _.isString], arguments, "CAA expects (name, tag, value) plus optional flag as a meta argument")

var mods = getModifiers(arguments,3)
mods.push({caatag: tag});

return function(d) {
addRecord(d,"CAA",name,value,mods)
}
}

var CAA = recordBuilder('CAA', {
args: [
['name', _.isString],
['tag', _.isString],
['value', _.isString],
],
transform: function(record, args, modifiers){
record.name = args.name;
record.caatag = args.tag;
record.target = args.value;
},
modifierNumber: function(record, value){
record.caaflags |= value;
},
});

// CNAME(name,target, recordModifiers...)
function CNAME(name, target) {
var mods = getModifiers(arguments,2)
return function(d) {
addRecord(d,"CNAME",name,target,mods)
}
}
var CNAME = recordBuilder('CNAME');

// PTR(name,target, recordModifiers...)
function PTR(name, target) {
var mods = getModifiers(arguments,2)
return function(d) {
addRecord(d,"PTR",name,target,mods)
}
}
var PTR = recordBuilder('PTR');

// SRV(name,priority,weight,port,target, recordModifiers...)
function SRV(name, priority, weight, port, target) {
checkArgs([_.isString, _.isNumber, _.isNumber, _.isNumber, _.isString], arguments, "SRV expects (name, priority, weight, port, target)")
var mods = getModifiers(arguments,5)
return function(d) {
addRecordSRV(d, "SRV", name, priority, weight, port, target, mods)
}
}
var SRV = recordBuilder('SRV', {
args: [
['name', _.isString],
['priority', _.isNumber],
['weight', _.isNumber],
['port', _.isNumber],
['target', _.isString],
],
transform: function(record, args, modifiers){
record.name = args.name;
record.srvpriority = args.priority;
record.srvweight = args.weight;
record.srvport = args.port;
record.target = args.target;
},
});

// TXT(name,target, recordModifiers...)
function TXT(name, target) {
var mods = getModifiers(arguments,2)
return function(d) {
addRecord(d,"TXT",name,target,mods)
}
}
var TXT = recordBuilder('TXT');

// MX(name,priority,target, recordModifiers...)
function MX(name, priority, target) {
checkArgs([_.isString, _.isNumber, _.isString], arguments, "MX expects (name, priority, target)")
var mods = getModifiers(arguments,3)
return function(d) {
mods.push(priority);
addRecord(d, "MX", name, target, mods)
}
}
var MX = recordBuilder('MX', {
args: [
['name', _.isString],
['priority', _.isNumber],
['target', _.isString],
],
transform: function(record, args, modifiers){
record.name = args.name;
record.mxpreference = args.priority;
record.target = args.target;
},
});

function checkArgs(checks, args, desc){
if (args.length < checks.length){
Expand All @@ -226,12 +219,7 @@ function checkArgs(checks, args, desc){
}

// NS(name,target, recordModifiers...)
function NS(name, target) {
var mods = getModifiers(arguments,2)
return function(d) {
addRecord(d,"NS",name,target,mods)
}
}
var NS = recordBuilder('NS');

// NAMESERVER(name,target)
function NAMESERVER(name, target) {
Expand Down Expand Up @@ -274,15 +262,19 @@ function format_tt(transform_table) {
}

// IMPORT_TRANSFORM(translation_table, domain)
function IMPORT_TRANSFORM(translation_table, domain,ttl) {
return function(d) {
var rec = addRecord(d, "IMPORT_TRANSFORM", "@", domain, [
{'transform_table': format_tt(translation_table)}])
if (ttl){
rec.ttl = ttl;
}
}
}
var IMPORT_TRANSFORM = recordBuilder('IMPORT_TRANSFORM', {
args: [
['translation_table'],
['domain'],
['ttl', _.isNumber],
],
transform: function(record, args, modifiers){
record.name = '@';
record.target = args.domain;
record.meta['transform_table'] = format_tt(args.translation_table);
record.ttl = args.ttl;
},
});

// PURGE()
function PURGE(d) {
Expand All @@ -294,6 +286,9 @@ function NO_PURGE(d) {
d.KeepUnknown = true
}

/**
* @deprecated
*/
function getModifiers(args,start) {
var mods = [];
for (var i = start;i<args.length; i++) {
Expand All @@ -302,6 +297,102 @@ function getModifiers(args,start) {
return mods;
}

/**
* Record type builder
* @param {string} type Record type
* @param {string} opts.args[][0] Argument name
* @param {function=} opts.args[][1] Optional validator
* @param {function=} opts.transform Function to apply arguments to record.
* Take (record, args, modifier) as arguments. Any modifiers will be
* applied before this function. It should mutate the given record.
* @param {function=} opts.applyModifier Function to apply modifiers to the record
*/
function recordBuilder(type, opts){
opts = _.defaults({}, opts, {
args: [
['name', _.isString],
['target'],
],

transform: function(record, args, modifiers) {
// record will have modifiers already applied
// args will be an object for parameters defined
record.name = args.name;
if (_.isNumber(args.target)) {
record.target = num2dot(args.target);
} else {
record.target = args.target;
}
},

applyModifier: function(record, modifiers) {
for (var i = 0; i < modifiers.length; i++) {
var mod = modifiers[i];

if (_.isFunction(mod)) {
mod(record);
} else if (_.isObject(mod)) {
// convert transforms to strings
if (mod.transform && _.isArray(mod.transform)) {
mod.transform = format_tt(mod.transform);
}
_.extend(record.meta, mod);
} else {
throw "ERROR: Unknown modifier type";
}
}
},
});

return function(){
var parsedArgs = {};
var modifiers = [];

if (arguments.length < opts.args.length) {
var argumentsList = opts.args.map(function(item){
return item[0];
}).join(', ');
throw type + " record requires " + opts.args.length + " arguments (" + argumentsList + "). Only " + arguments.length + " were supplied";
return;
}

// collect arguments
for (var i = 0; i < opts.args.length; i++) {
var argDefinition = opts.args[i];
var value = arguments[i];
if (argDefinition.length > 1) {
// run validator if supplied
if(!argDefinition[1](value)){
throw type + " record " + argDefinition[0] + " argument validation failed";
}
}
parsedArgs[argDefinition[0]] = value;
}

// collect modifiers
for (var i = opts.args.length; i < arguments.length; i++) {
modifiers.push(arguments[i]);
}

return function(d){
var record = {
type: type,
meta: {},
ttl: d.defaultTTL,
};

opts.applyModifier(record, modifiers);
opts.transform(record, parsedArgs, modifiers);

d.records.push(record);
return record;
};
};
}

/**
* @deprecated
*/
function addRecord(d,type,name,target,mods) {
// if target is number, assume ip address. convert it.
if (_.isNumber(target)) {
Expand All @@ -312,40 +403,6 @@ function addRecord(d,type,name,target,mods) {
// - Function: call is with the record as the argument
// - Object: merge it into the metadata
// - Number: IF MX record assume it is priority
if (mods) {
for (var i = 0; i< mods.length; i++) {
var m = mods[i]
if (_.isFunction(m)) {
m(rec);
} else if (_.isObject(m) && m.caatag) {
// caatag is a top level object, not in meta
rec.caatag = m.caatag;
} else if (_.isObject(m)) {
//convert transforms to strings
if (m.transform && _.isArray(m.transform)){
m.transform = format_tt(m.transform)
}
_.extend(rec.meta,m);
_.extend(rec.meta,m);
} else if (_.isNumber(m) && type == "MX") {
rec.mxpreference = m;
} else if (_.isNumber(m) && type == "CAA") {
rec.caaflags |= m;
} else {
console.log("WARNING: Modifier type unsupported:", typeof m, "(Skipping!)");
}
}
}
d.records.push(rec);
return rec;
}

function addRecordSRV(d,type,name,srvpriority,srvweight,srvport,target,mods) {
var rec = {type: type, name: name, srvpriority: srvpriority, srvweight: srvweight, srvport: srvport, target: target, ttl:d.defaultTTL, meta:{}};
// for each modifier, decide based on type:
// - Function: call is with the record as the argument
// - Object: merge it into the metadata
// FIXME(tlim): Factor this code out to its own function.
if (mods) {
for (var i = 0; i< mods.length; i++) {
var m = mods[i]
Expand Down Expand Up @@ -406,19 +463,32 @@ var CF_PROXY_DEFAULT_OFF = {'cloudflare_proxy_default': 'off'};
var CF_PROXY_DEFAULT_ON = {'cloudflare_proxy_default': 'on'};

// CUSTOM, PROVIDER SPECIFIC RECORD TYPES
function CF_REDIRECT(src, dst) {
return function(d) {
if (src.indexOf(",") !== -1 || dst.indexOf(",") !== -1){
throw("redirect src and dst must not have commas")
}
addRecord(d,"CF_REDIRECT","@",src+","+dst)
}
}
function CF_TEMP_REDIRECT(src, dst) {
return function(d) {
if (src.indexOf(",") !== -1 || dst.indexOf(",") !== -1){
throw("redirect src and dst must not have commas")
}
addRecord(d,"CF_TEMP_REDIRECT","@",src+","+dst)
}
}

function _validateCloudFlareRedirect(value){
if(!_.isString(value)){
return false;
}
return value.indexOf(",") === -1;
}

var CF_REDIRECT = recordBuilder("CF_REDIRECT", {
args: [
["source", _validateCloudFlareRedirect],
["destination", _validateCloudFlareRedirect],
],
transform: function(record, args, modifiers){
record.name = "@";
record.target = args.source + "," + args.destination;
},
});

var CF_TEMP_REDIRECT = recordBuilder("CF_TEMP_REDIRECT", {
args: [
["source", _validateCloudFlareRedirect],
["destination", _validateCloudFlareRedirect],
],
transform: function(record, args, modifiers){
record.name = "@";
record.target = args.source + "," + args.destination;
},
});
Loading

0 comments on commit ab70c36

Please # to comment.