kayvee 3.17.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -202
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/kayvee.d.ts +12 -0
- package/dist/kayvee.d.ts.map +1 -0
- package/dist/kayvee.js +50 -0
- package/dist/logger/logger.d.ts +49 -0
- package/dist/logger/logger.d.ts.map +1 -0
- package/dist/logger/logger.js +237 -0
- package/dist/middleware.d.ts +23 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +196 -0
- package/dist/package.json +89 -0
- package/dist/router/index.d.ts +23 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +184 -0
- package/package.json +64 -24
- package/.circleci/config.yml +0 -25
- package/.eslintrc.yml +0 -44
- package/.nvmrc +0 -1
- package/.prettierrc.json +0 -1
- package/Makefile +0 -56
- package/benchmarks/data/.keep +0 -1
- package/benchmarks/data/corpus-basic.json +0 -22
- package/benchmarks/data/corpus-pathological.json +0 -22
- package/benchmarks/data/corpus-realistic.json +0 -22
- package/benchmarks/data/kvconfig-basic.yml +0 -7
- package/benchmarks/data/kvconfig-pathological.yml +0 -222
- package/benchmarks/data/kvconfig-realistic.yml +0 -39
- package/benchmarks/routing.js +0 -116
- package/build/lib/kayvee.js +0 -67
- package/build/lib/logger/helpers.js +0 -0
- package/build/lib/logger/logger.js +0 -221
- package/build/lib/middleware.js +0 -302
- package/build/lib/router/index.js +0 -198
- package/build/package.json +0 -49
- package/build/test/context_logger.js +0 -77
- package/build/test/kayvee.js +0 -36
- package/build/test/logger_test.js +0 -334
- package/build/test/middleware.js +0 -557
- package/build/test/router.js +0 -311
- package/index.js +0 -7
- package/lib/kayvee.ts +0 -73
- package/lib/logger/helpers.ts +0 -0
- package/lib/logger/logger.ts +0 -296
- package/lib/middleware.ts +0 -317
- package/lib/router/index.ts +0 -234
- package/lib/router/schema_definitions.json +0 -158
- package/test/context_logger.ts +0 -76
- package/test/kayvee.ts +0 -50
- package/test/kvconfig.yml +0 -14
- package/test/logger_test.ts +0 -378
- package/test/middleware.ts +0 -632
- package/test/router.ts +0 -558
- package/test/static/empty.css +0 -0
- package/test/static/js/empty.js +0 -0
- package/test/tests.json +0 -100
- package/tsconfig.json +0 -10
- package/tslint.json +0 -134
- /package/{build/lib → dist}/router/schema_definitions.json +0 -0
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
var fs = require("fs");
|
|
2
|
-
var jsonschema = require("jsonschema");
|
|
3
|
-
var schema = require("./schema_definitions");
|
|
4
|
-
var yaml = require("js-yaml");
|
|
5
|
-
var _ = require("underscore");
|
|
6
|
-
var packageJson = require("../../package.json");
|
|
7
|
-
var kvVersion = packageJson.version;
|
|
8
|
-
var teamName = process.env._TEAM_OWNER || "UNSET";
|
|
9
|
-
var reEnvvarTokens = new RegExp("\\$\\{(.+?)\\}", "g");
|
|
10
|
-
var reFieldTokens = new RegExp("%\\{(.+?)\\}", "g");
|
|
11
|
-
// For performance reason this code is intentionally redundant and not-inlined.
|
|
12
|
-
// Removing redundancy and inlining this function some how makes performance worst.
|
|
13
|
-
function substituteEnvVars(obj, subber) {
|
|
14
|
-
var rtn = {};
|
|
15
|
-
var replacer = function (s) { return s.replace(reEnvvarTokens, function (__, p1) { return subber(p1); }); };
|
|
16
|
-
for (var key in obj) {
|
|
17
|
-
var val = obj[key];
|
|
18
|
-
if (Array.isArray(val)) {
|
|
19
|
-
var updatedVals = Array(val.length);
|
|
20
|
-
for (var i = 0; i < val.length; i++) {
|
|
21
|
-
updatedVals[i] = replacer(val[i]);
|
|
22
|
-
}
|
|
23
|
-
rtn[key] = updatedVals;
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
rtn[key] = replacer(val);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return rtn;
|
|
30
|
-
}
|
|
31
|
-
function deepKey(obj, key) {
|
|
32
|
-
var path = key.split(".");
|
|
33
|
-
var idx = 0;
|
|
34
|
-
var val = obj;
|
|
35
|
-
do {
|
|
36
|
-
val = val[path[idx++]];
|
|
37
|
-
} while (val && idx < path.length);
|
|
38
|
-
return val;
|
|
39
|
-
}
|
|
40
|
-
function fieldMatches(obj, field, values) {
|
|
41
|
-
var val = obj[field] || deepKey(obj, field);
|
|
42
|
-
if (val == null || val === "") {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
if (values[0] === "*") {
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
for (var i = 0; i < values.length; i++) {
|
|
49
|
-
if (values[i] === val) {
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
var Rule = /** @class */ (function () {
|
|
56
|
-
function Rule(name, matchers, output) {
|
|
57
|
-
this.name = null;
|
|
58
|
-
this.matchers = null;
|
|
59
|
-
this.output = null;
|
|
60
|
-
this.name = name;
|
|
61
|
-
this.matchers = matchers;
|
|
62
|
-
var envMissing = [];
|
|
63
|
-
this.output = substituteEnvVars(output, function (k) {
|
|
64
|
-
var val = process.env[k];
|
|
65
|
-
if (val == null) {
|
|
66
|
-
envMissing.push(k);
|
|
67
|
-
}
|
|
68
|
-
return val;
|
|
69
|
-
});
|
|
70
|
-
if (envMissing.length > 0) {
|
|
71
|
-
throw new Error("Missing env var(s): " + envMissing.join(", "));
|
|
72
|
-
}
|
|
73
|
-
Object.keys(matchers).forEach(function (field) {
|
|
74
|
-
var fieldVals = matchers[field];
|
|
75
|
-
if (fieldVals.indexOf("*") !== -1 && fieldVals.length > 1) {
|
|
76
|
-
throw new Error("Invalid matcher values in " + name + "." + field + ".\n" +
|
|
77
|
-
"Wildcard matcher can't co-exist with other matchers.");
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
if (this.output.type === "alerts" || this.output.type === "metrics") {
|
|
81
|
-
this.output.value_field = this.output.value_field || "value";
|
|
82
|
-
}
|
|
83
|
-
this.output.rule = this.name;
|
|
84
|
-
}
|
|
85
|
-
// matches returns true if `msg` matches against this rule
|
|
86
|
-
Rule.prototype.matches = function (msg) {
|
|
87
|
-
for (var field in this.matchers) {
|
|
88
|
-
if (!fieldMatches(msg, field, this.matchers[field])) {
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return true;
|
|
93
|
-
};
|
|
94
|
-
// returns the output with kv substitutions performed
|
|
95
|
-
Rule.prototype.outputFor = function (msg) {
|
|
96
|
-
var rtn = {};
|
|
97
|
-
var subst = function (__, k) { return msg[k] || deepKey(msg, k) || "KEY_NOT_FOUND"; };
|
|
98
|
-
var replacer = function (s) { return s.replace(reFieldTokens, subst); };
|
|
99
|
-
for (var key in this.output) {
|
|
100
|
-
var val = this.output[key];
|
|
101
|
-
if (Array.isArray(val)) {
|
|
102
|
-
var updatedVals = Array(val.length);
|
|
103
|
-
for (var i = 0; i < val.length; i++) {
|
|
104
|
-
updatedVals[i] = replacer(val[i]);
|
|
105
|
-
}
|
|
106
|
-
rtn[key] = updatedVals;
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
rtn[key] = replacer(val);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return rtn;
|
|
113
|
-
};
|
|
114
|
-
return Rule;
|
|
115
|
-
}());
|
|
116
|
-
// validateKVConfig ensures that `routes` matches the config schema. We have this
|
|
117
|
-
// function instead of just doing a plain jsonschema.validate in order to get
|
|
118
|
-
// better error messages for the "output" object (by default jsonschema would
|
|
119
|
-
// just tell you that the output block doesn't match any of the known output
|
|
120
|
-
// formats, but won't tell you what's wrong because it doesn't let you
|
|
121
|
-
// condition on the output.type property).
|
|
122
|
-
function validateKVConfig(config) {
|
|
123
|
-
var validator = new jsonschema.Validator();
|
|
124
|
-
var results = validator.validate(config, schema);
|
|
125
|
-
return {
|
|
126
|
-
valid: results.valid,
|
|
127
|
-
errors: results.errors.map(function (err) { return err.stack; }),
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
// parseConfig parses and validates the configuration passed as a string. It
|
|
131
|
-
// returns an object of the form {valid, rules, errors}, where valid is true if
|
|
132
|
-
// it was successfully parsed, rules is an array of rules, and errors is an
|
|
133
|
-
// array of errors.
|
|
134
|
-
function parseConfig(fileString) {
|
|
135
|
-
var config;
|
|
136
|
-
try {
|
|
137
|
-
config = yaml.safeLoad(fileString);
|
|
138
|
-
}
|
|
139
|
-
catch (e) {
|
|
140
|
-
return { valid: false, rules: [], errors: [e] };
|
|
141
|
-
}
|
|
142
|
-
var validateRes = validateKVConfig(config);
|
|
143
|
-
if (!validateRes.valid) {
|
|
144
|
-
return _.assign(validateRes, { rules: [] });
|
|
145
|
-
}
|
|
146
|
-
try {
|
|
147
|
-
var rulesObj = _.mapObject(config.routes, function (elem, name) { return new Rule(name, elem.matchers, elem.output); });
|
|
148
|
-
var rules = _.values(rulesObj);
|
|
149
|
-
return { valid: true, rules: rules, errors: [] };
|
|
150
|
-
}
|
|
151
|
-
catch (e) {
|
|
152
|
-
return { valid: false, rules: [], errors: [e] };
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
var Router = /** @class */ (function () {
|
|
156
|
-
function Router(rules) {
|
|
157
|
-
this.rules = null;
|
|
158
|
-
this.rules = rules || [];
|
|
159
|
-
}
|
|
160
|
-
// loadConfig reads in the config located at `filename` and sets the routing
|
|
161
|
-
// rules to what it finds there. It should be a YAML-formatted file with
|
|
162
|
-
// routing rules placed under the `routes` key on the root object.
|
|
163
|
-
Router.prototype.loadConfig = function (filename) {
|
|
164
|
-
var data = fs.readFileSync(filename, "utf8");
|
|
165
|
-
this._loadConfigString(data);
|
|
166
|
-
};
|
|
167
|
-
Router.prototype._loadConfigString = function (configStr) {
|
|
168
|
-
var parsedRules = parseConfig(configStr);
|
|
169
|
-
if (!parsedRules.valid) {
|
|
170
|
-
throw new Error(parsedRules.errors);
|
|
171
|
-
}
|
|
172
|
-
this.rules = parsedRules.rules;
|
|
173
|
-
};
|
|
174
|
-
// route matches the log line `msg` against all loaded rules and returns a
|
|
175
|
-
// metadata object describing the outputs it should be sent to based on that
|
|
176
|
-
// matching. logger.ts will attach this to log lines under the `_kvmeta`
|
|
177
|
-
// property.
|
|
178
|
-
Router.prototype.route = function (msg) {
|
|
179
|
-
var outputs = [];
|
|
180
|
-
for (var i = 0; i < this.rules.length; i++) {
|
|
181
|
-
var rule = this.rules[i];
|
|
182
|
-
if (rule.matches(msg)) {
|
|
183
|
-
outputs.push(rule.outputFor(msg));
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return {
|
|
187
|
-
team: teamName,
|
|
188
|
-
kv_version: kvVersion,
|
|
189
|
-
kv_language: "js",
|
|
190
|
-
routes: outputs,
|
|
191
|
-
};
|
|
192
|
-
};
|
|
193
|
-
return Router;
|
|
194
|
-
}());
|
|
195
|
-
module.exports = {
|
|
196
|
-
Router: Router,
|
|
197
|
-
Rule: Rule,
|
|
198
|
-
};
|
package/build/package.json
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "kayvee",
|
|
3
|
-
"description": "Write data to key=val pairs, for human and machine readability",
|
|
4
|
-
"version": "3.17.0",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"repository": {
|
|
7
|
-
"type": "git",
|
|
8
|
-
"url": "git://github.com/Clever/kayvee-js"
|
|
9
|
-
},
|
|
10
|
-
"dependencies": {
|
|
11
|
-
"js-yaml": "^3.6.1",
|
|
12
|
-
"jsonschema": "^1.1.0",
|
|
13
|
-
"morgan": "^1.7.0",
|
|
14
|
-
"qs": "^6.9.4",
|
|
15
|
-
"split": "^1.0.0",
|
|
16
|
-
"underscore": "^1.8.3"
|
|
17
|
-
},
|
|
18
|
-
"devDependencies": {
|
|
19
|
-
"@clever/prettier-config": "1.0.0",
|
|
20
|
-
"@types/mocha": "^8.0.3",
|
|
21
|
-
"@types/node": "^12.12.68",
|
|
22
|
-
"babel-eslint": "^6.0.2",
|
|
23
|
-
"benchmark": "^2.1.1",
|
|
24
|
-
"eslint": "^2.7.0",
|
|
25
|
-
"eslint-config-airbnb": "^7.0.0",
|
|
26
|
-
"eslint-plugin-jsx-a11y": "^0.6.2",
|
|
27
|
-
"eslint-plugin-react": "^4.3.0",
|
|
28
|
-
"express": "^4.13.4",
|
|
29
|
-
"mocha": "^3.5.3",
|
|
30
|
-
"prettier": "2.1.2",
|
|
31
|
-
"sinon": "^1.17.4",
|
|
32
|
-
"supertest": "^1.2.0",
|
|
33
|
-
"ts-node": "^9.0.0",
|
|
34
|
-
"tslint": "^3.7.4",
|
|
35
|
-
"typescript": "^4.0.3"
|
|
36
|
-
},
|
|
37
|
-
"scripts": {
|
|
38
|
-
"test": "make -Br test",
|
|
39
|
-
"prepublish": "make build"
|
|
40
|
-
},
|
|
41
|
-
"author": "Clever <tech-notify@clever.com>",
|
|
42
|
-
"license": "BSD-2-Clause",
|
|
43
|
-
"bugs": {
|
|
44
|
-
"url": "https://github.com/Clever/kayvee-js/issues"
|
|
45
|
-
},
|
|
46
|
-
"publishConfig": {
|
|
47
|
-
"registry": "https://registry.npmjs.org"
|
|
48
|
-
}
|
|
49
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
var assert = require("assert");
|
|
2
|
-
var sinon = require("sinon");
|
|
3
|
-
var middleware = require("../lib/middleware");
|
|
4
|
-
var KayveeLogger = require("../lib/logger/logger");
|
|
5
|
-
describe("ContextLogger", function () {
|
|
6
|
-
var fake_req = { key1: "val1" };
|
|
7
|
-
var fake_handler = function (req) { return ({ log_key1: req.key1, key2: "val2" }); };
|
|
8
|
-
var _loop_1 = function (level) {
|
|
9
|
-
it("correctly adds context to " + level + " calls", function () {
|
|
10
|
-
var spy = sinon.spy();
|
|
11
|
-
var stub_logger = {};
|
|
12
|
-
stub_logger[level + "D"] = spy;
|
|
13
|
-
var log = new middleware.ContextLogger(stub_logger, [fake_handler], fake_req);
|
|
14
|
-
log[level]("test_title");
|
|
15
|
-
var expected_context = { log_key1: "val1", key2: "val2" };
|
|
16
|
-
assert(spy.calledWithExactly("test_title", expected_context));
|
|
17
|
-
assert.equal(spy.callCount, 1);
|
|
18
|
-
});
|
|
19
|
-
it("correctly adds context to " + level + "D calls", function () {
|
|
20
|
-
var spy = sinon.spy();
|
|
21
|
-
var stub_logger = {};
|
|
22
|
-
stub_logger[level + "D"] = spy;
|
|
23
|
-
var log = new middleware.ContextLogger(stub_logger, [fake_handler], fake_req);
|
|
24
|
-
log[level + "D"]("test_title", { key2: "new_value", key3: "val3" });
|
|
25
|
-
var expected_data = {
|
|
26
|
-
log_key1: "val1",
|
|
27
|
-
key2: "new_value",
|
|
28
|
-
key3: "val3",
|
|
29
|
-
};
|
|
30
|
-
assert(spy.calledWithExactly("test_title", expected_data));
|
|
31
|
-
assert.equal(spy.callCount, 1);
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
|
-
for (var _i = 0, _a = KayveeLogger.LEVELS; _i < _a.length; _i++) {
|
|
35
|
-
var level = _a[_i];
|
|
36
|
-
_loop_1(level);
|
|
37
|
-
}
|
|
38
|
-
var _loop_2 = function (metric) {
|
|
39
|
-
it("correctly adds context to " + metric + " calls", function () {
|
|
40
|
-
var spy = sinon.spy();
|
|
41
|
-
var stub_logger = {};
|
|
42
|
-
stub_logger[metric + "D"] = spy;
|
|
43
|
-
var log = new middleware.ContextLogger(stub_logger, [fake_handler], fake_req);
|
|
44
|
-
log[metric]("test_title", 3);
|
|
45
|
-
var expected_context = { log_key1: "val1", key2: "val2" };
|
|
46
|
-
assert(spy.calledWithExactly("test_title", 3, expected_context));
|
|
47
|
-
assert.equal(spy.callCount, 1);
|
|
48
|
-
});
|
|
49
|
-
it("correctly adds context to " + metric + "D calls", function () {
|
|
50
|
-
var spy = sinon.spy();
|
|
51
|
-
var stub_logger = {};
|
|
52
|
-
stub_logger[metric + "D"] = spy;
|
|
53
|
-
var log = new middleware.ContextLogger(stub_logger, [fake_handler], fake_req);
|
|
54
|
-
log[metric + "D"]("test_title", 3, { key2: "new_value", key3: "val3" });
|
|
55
|
-
var expected_data = {
|
|
56
|
-
log_key1: "val1",
|
|
57
|
-
key2: "new_value",
|
|
58
|
-
key3: "val3",
|
|
59
|
-
};
|
|
60
|
-
assert(spy.calledWithExactly("test_title", 3, expected_data));
|
|
61
|
-
assert.equal(spy.callCount, 1);
|
|
62
|
-
});
|
|
63
|
-
};
|
|
64
|
-
for (var _b = 0, _c = KayveeLogger.METRICS; _b < _c.length; _b++) {
|
|
65
|
-
var metric = _c[_b];
|
|
66
|
-
_loop_2(metric);
|
|
67
|
-
}
|
|
68
|
-
it("correctly handles being instantiated with empty list of handlers", function () {
|
|
69
|
-
var spy = sinon.spy();
|
|
70
|
-
var stub_logger = { infoD: spy };
|
|
71
|
-
var log = new middleware.ContextLogger(stub_logger, [], fake_req);
|
|
72
|
-
log.info("test_title");
|
|
73
|
-
var expected_context = {};
|
|
74
|
-
assert(spy.calledWithExactly("test_title", expected_context));
|
|
75
|
-
assert.equal(spy.callCount, 1);
|
|
76
|
-
});
|
|
77
|
-
});
|
package/build/test/kayvee.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
var kv = require("../lib/kayvee");
|
|
2
|
-
var assert = require("assert");
|
|
3
|
-
var _ = require("underscore");
|
|
4
|
-
var fs = require("fs");
|
|
5
|
-
describe("kayvee", function () {
|
|
6
|
-
var tests = JSON.parse(fs.readFileSync("test/tests.json"));
|
|
7
|
-
describe(".format", function () {
|
|
8
|
-
_.each(tests.format, function (spec) {
|
|
9
|
-
it(spec.title, function () {
|
|
10
|
-
var actual = kv.format(spec.input.data);
|
|
11
|
-
var expected = spec.output;
|
|
12
|
-
assert.deepEqual(JSON.parse(actual), _.extend({ deploy_env: "testing", wf_id: "abc" }, JSON.parse(expected)));
|
|
13
|
-
});
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
describe(".format with Errors", function () {
|
|
17
|
-
it("encodes Error objects", function () {
|
|
18
|
-
var actual = kv.format({ err: Error("An Error Message") });
|
|
19
|
-
var expected = {
|
|
20
|
-
deploy_env: "testing",
|
|
21
|
-
wf_id: "abc",
|
|
22
|
-
err: "Error: An Error Message",
|
|
23
|
-
};
|
|
24
|
-
assert.deepEqual(JSON.parse(actual), expected);
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
describe(".formatLog", function () {
|
|
28
|
-
_.each(tests.formatLog, function (spec) {
|
|
29
|
-
it(spec.title, function () {
|
|
30
|
-
var actual = kv.formatLog(spec.input.source, spec.input.level, spec.input.title, spec.input.data);
|
|
31
|
-
var expected = spec.output;
|
|
32
|
-
assert.deepEqual(JSON.parse(actual), _.extend({ deploy_env: "testing", wf_id: "abc" }, JSON.parse(expected)));
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
});
|