UNPKG

glutenfree

Version:

A profiler/loganalyzer for nginx/Cetrea Aw.

218 lines (180 loc) 7.6 kB
Q = require("q") http = require("http") url = require("url") fs = require("fs") xml2js = require("xml2js") _ = require("underscore") winston = require("winston") util = require("util") class ArgumentGenerator # @server is server # @cache is a type.prop, values cache, e.g. BedPlace.Oid: [BedPlace.1, BedPlace.2] # @premapped is a map of readymade conversions # @mappers is a list of mappers, if none are provide auto-discovery is set in motion constructor: (@server, @lookupConfiguration, @cache = {}, @premapped = {}, @mappers, @targeting) -> @version = "0.2.1" # find available mappers @parser = new xml2js.Parser({ ignoreAttrs: true }) # if empty mappers list is supplied winston.info "loading mappers" if not @mappers? @mappers = {} for file in fs.readdirSync("ArgumentMappers") when file.match(/.*\.js/) do (file) => m = require("./ArgumentMappers/#{file}").mapper @mappers[m.identifier] = m winston.info "installed mapper for '#{m.identifier}'" winston.info "loading targeting" if not @targeting? @targeting = {} for file in fs.readdirSync("ProfilerTargeting") when file.match(/.*\.js/) do (file) => t = require("./ProfilerTargeting/#{file}").targeting @targeting[t.name] = t winston.info "installed targeting '#{t.name}'" # map from given arguments to ones matching the given server map: (method, action, component, componentVersion, endpoint, fun, args) -> cached = @premapped["#{method}.#{component}.#{componentVersion}.#{endpoint}.#{fun}.#{args}"] if cached? then return Q.fcall(->cached) # retrieve mapper for endpoint mapperId = [component, componentVersion, endpoint].filter((c) -> c?).join("/") mapper = @mappers[mapperId] # if no mapper for endpoint is found simple return url if not mapper? winston.warn "No mapper for #{mapperId} found. Original args returned." return Q.fcall(->args) # get schema for function schema = mapper.schema(method, endpoint, action, fun, decodeURI(args)) if not schema? winston.warn "No schema returned for '#{method} #{action} (#{component}/#{componentVersion}) #{endpoint}/#{fun}/#{args}'. Original args returned." return Q.fcall(->args) # walk through schema and find arguments from 1) cache, 2) server promises = _.map( _.flatten(schema), (arg) => switch arg.origin when "mapi" cached = @cache["#{arg.type}.#{arg.hql}.#{arg.depth}"] value = if cached? then cached if not value? deferred = Q.defer() #deferred.promise.then (values) -> arg.newvalue = values #@cache["#{arg.type}.#{arg.hql}.#{arg.depth}"] = deferred.promise options = host: @server port: 8080 path: "/#{arg.type}?depth=#{arg.depth}&limit=200&hql=active=true" + (if arg.hql? then " AND "+arg.hql else "") auth: "spider:spider" headers: { "User-Agent": "GLUTENFREE (AG-#{@version})" } # go through server http.get(options, (res) => chunks = "" res.on("data", (chunk) -> chunks += chunk ) res.on("end", () => console.log "get", options # xml2js the shit out of this chunked mofo @parser.parseString( chunks, (err, data) => values = _.flatten(_.values(data.Collection)) # console.log "values", values # save result in @cache @cache["#{arg.type}.#{arg.hql}.#{arg.depth}"] = values # save result in arg.newval arg.newvalue = values # done, resolve deferred.resolve(arg) ) ) ).on("error", (e) => winston.error e deferred.reject("error when getting #{arg.type}.#{arg.hql}.#{arg.depth}") ) # return return deferred.promise else # argument was cached arg.newvalue = value arg when "configuration", "conf" # load and match from configuration if @lookupConfiguration? newvalue = [] for currentvalue in arg.currentvalue do (currentvalue) => v = @lookupConfiguration[arg.type][currentvalue] if not v? # if no explicit mapping then pick random value v = _.first(_.shuffle(_.values(@lookupConfiguration[arg.type]))) newvalue.push(v) arg.newvalue = newvalue else arg.newvalue = arg.currentvalue # return arg arg when "fixed" # origin is not mapi arg.newvalue = arg.currentvalue arg ) # wait for all promises to be fullfulled then let mapper generate url Q.allSettled(promises).then( => # run through all resolved and pluck property for schematic in _.flatten(schema) do (schematic) -> if schematic.origin is "mapi" candidates = if schematic.filter? then _.filter(schematic.newvalue, (t) -> schematic.filter(t, schema)) else schematic.newvalue schematic.newvalue = _.first(_.shuffle(_.flatten(_.pluck(candidates,schematic.property))),schematic.currentvalue.length) url = mapper.applySchema(method, action, endpoint, fun, schema) @premapped["#{method}.#{component}.#{componentVersion}.#{endpoint}.#{fun}.#{args}"] = url console.log "url (pre-encode)", url encodeURI(url) ) exports.AG = ArgumentGenerator # if run as main module if require.main is module argv = require('optimist') .usage("Usage: $0 -i [inputfile] -o [outputfile] -s [server] -t [targeting] -c [configuration]") .alias("s", "server") .demand(["i","o","s"]) .alias("c", "configiration") .argv statsFile = if argv.i[0] is "/" then argv.i else "./#{argv.i}" stats = require(statsFile) lookupConf = if argv.c? if argv.c[0] is "/" then require(argv.c) else require("./#{argv.c}") else {} winston.info "creating ag" ag = new ArgumentGenerator(argv.s, lookupConf) winston.info "looping stats" # deep loop promises = [] for id, info of stats.uniques do (id, info) -> info.newArgs = [] promises.push( ag.map(info.method, info.action, info.component, info.componentVersion, info.endpoint, info.fun, info.args) .then(((newarg) -> info.newArgs.push newarg)) ) Q.all(promises) .then( -> # targeting targeting = ag.targeting[argv.t] if not targeting? fs.writeFile( argv.o, JSON.stringify(stats), (err) -> if not err? then winston.info "raw output written to #{argv.o}" else winston.warn "error writing to #{argv.o}" ) else targets = targeting.generate(stats) fs.writeFile( argv.o, JSON.stringify(targets), (err) -> if not err? then winston.info "targeting (#{targeting.name}) output written to #{argv.o}" else winston.warn "error writing to #{argv.o}" ) )