@fintekkers/ledger-models 0.4.30 → 0.4.32
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/node/wrappers/models/price/price_rules.d.ts +20 -0
- package/node/wrappers/models/price/price_rules.js +53 -0
- package/node/wrappers/models/price/price_rules.js.map +1 -0
- package/node/wrappers/models/price/price_rules.test.d.ts +1 -0
- package/node/wrappers/models/price/price_rules.test.js +112 -0
- package/node/wrappers/models/price/price_rules.test.js.map +1 -0
- package/node/wrappers/models/price/price_rules.test.ts +128 -0
- package/node/wrappers/models/price/price_rules.ts +50 -0
- package/node/wrappers/models/utils/requestcontext.auth.test.js +21 -6
- package/node/wrappers/models/utils/requestcontext.auth.test.js.map +1 -1
- package/node/wrappers/models/utils/requestcontext.auth.test.ts +24 -6
- package/node/wrappers/models/utils/requestcontext.d.ts +18 -12
- package/node/wrappers/models/utils/requestcontext.js +28 -28
- package/node/wrappers/models/utils/requestcontext.js.map +1 -1
- package/node/wrappers/models/utils/requestcontext.test.js +15 -3
- package/node/wrappers/models/utils/requestcontext.test.js.map +1 -1
- package/node/wrappers/models/utils/requestcontext.test.ts +17 -3
- package/node/wrappers/models/utils/requestcontext.ts +31 -29
- package/node/wrappers/services/apikey.credentials.test.d.ts +1 -1
- package/node/wrappers/services/apikey.credentials.test.js +14 -12
- package/node/wrappers/services/apikey.credentials.test.js.map +1 -1
- package/node/wrappers/services/apikey.credentials.test.ts +15 -13
- package/node/wrappers/services/apikey.test.js +1 -1
- package/node/wrappers/services/apikey.test.js.map +1 -1
- package/node/wrappers/services/apikey.test.ts +1 -1
- package/node/wrappers/services/portfolio-service/PortfolioService.d.ts +2 -1
- package/node/wrappers/services/portfolio-service/PortfolioService.js +8 -4
- package/node/wrappers/services/portfolio-service/PortfolioService.js.map +1 -1
- package/node/wrappers/services/portfolio-service/PortfolioService.ts +8 -4
- package/node/wrappers/services/position-service/PositionService.js +4 -3
- package/node/wrappers/services/position-service/PositionService.js.map +1 -1
- package/node/wrappers/services/position-service/PositionService.ts +4 -3
- package/node/wrappers/services/price-service/PriceService.js +4 -3
- package/node/wrappers/services/price-service/PriceService.js.map +1 -1
- package/node/wrappers/services/price-service/PriceService.ts +4 -3
- package/node/wrappers/services/security-service/SecurityService.js +4 -3
- package/node/wrappers/services/security-service/SecurityService.js.map +1 -1
- package/node/wrappers/services/security-service/SecurityService.ts +4 -3
- package/node/wrappers/services/serviceaddress.clients.test.d.ts +9 -0
- package/node/wrappers/services/serviceaddress.clients.test.js +181 -0
- package/node/wrappers/services/serviceaddress.clients.test.js.map +1 -0
- package/node/wrappers/services/serviceaddress.clients.test.ts +176 -0
- package/node/wrappers/services/transaction-service/TransactionService.js +4 -3
- package/node/wrappers/services/transaction-service/TransactionService.js.map +1 -1
- package/node/wrappers/services/transaction-service/TransactionService.ts +4 -3
- package/node/wrappers/util/link-resolver.d.ts +1 -1
- package/node/wrappers/util/link-resolver.js +10 -9
- package/node/wrappers/util/link-resolver.js.map +1 -1
- package/node/wrappers/util/link-resolver.ts +11 -10
- package/node/wrappers/util/serviceaddress.d.ts +47 -0
- package/node/wrappers/util/serviceaddress.js +132 -0
- package/node/wrappers/util/serviceaddress.js.map +1 -0
- package/node/wrappers/util/serviceaddress.test.d.ts +6 -0
- package/node/wrappers/util/serviceaddress.test.js +76 -0
- package/node/wrappers/util/serviceaddress.test.js.map +1 -0
- package/node/wrappers/util/serviceaddress.test.ts +83 -0
- package/node/wrappers/util/serviceaddress.ts +141 -0
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PriceProto } from '../../../fintekkers/models/price/price_pb';
|
|
2
|
+
import { FieldViolationProto } from '../../../fintekkers/requests/util/errors/field_violation_pb';
|
|
3
|
+
/**
|
|
4
|
+
* Price input rules (LM-289): JS counterpart of Java's PriceRules
|
|
5
|
+
* (docs/adr/typed-input-errors.md). Pure: proto in, violations out.
|
|
6
|
+
*
|
|
7
|
+
* Three rules, checked in this order: `security` has a non-empty UUID,
|
|
8
|
+
* `price` has a non-empty arbitrary_precision_value (zero is valid, see the
|
|
9
|
+
* PriceProto comment), and `price_type` is not UNKNOWN_PRICE_TYPE. The price
|
|
10
|
+
* value is not parsed. Never throws on bad or unset input; null or undefined
|
|
11
|
+
* is checked as an empty PriceProto. Unlike security_rules, a link price is
|
|
12
|
+
* not skipped: hydrate it first. The Price wrapper does not call these rules.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SECURITY = "security";
|
|
15
|
+
export declare const PRICE = "price";
|
|
16
|
+
export declare const PRICE_TYPE = "price_type";
|
|
17
|
+
/** Returns every field-level violation on this price, or an empty array when it is valid. */
|
|
18
|
+
export declare function validate(proto: PriceProto | null | undefined): FieldViolationProto[];
|
|
19
|
+
/** Throws ModelValidationError carrying every violation if `validate` finds any. */
|
|
20
|
+
export declare function requireValid(proto: PriceProto | null | undefined): void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requireValid = exports.validate = exports.PRICE_TYPE = exports.PRICE = exports.SECURITY = void 0;
|
|
4
|
+
const price_pb_1 = require("../../../fintekkers/models/price/price_pb");
|
|
5
|
+
const price_type_pb_1 = require("../../../fintekkers/models/price/price_type_pb");
|
|
6
|
+
const errors_1 = require("../errors");
|
|
7
|
+
const uuid_1 = require("../utils/uuid");
|
|
8
|
+
/**
|
|
9
|
+
* Price input rules (LM-289): JS counterpart of Java's PriceRules
|
|
10
|
+
* (docs/adr/typed-input-errors.md). Pure: proto in, violations out.
|
|
11
|
+
*
|
|
12
|
+
* Three rules, checked in this order: `security` has a non-empty UUID,
|
|
13
|
+
* `price` has a non-empty arbitrary_precision_value (zero is valid, see the
|
|
14
|
+
* PriceProto comment), and `price_type` is not UNKNOWN_PRICE_TYPE. The price
|
|
15
|
+
* value is not parsed. Never throws on bad or unset input; null or undefined
|
|
16
|
+
* is checked as an empty PriceProto. Unlike security_rules, a link price is
|
|
17
|
+
* not skipped: hydrate it first. The Price wrapper does not call these rules.
|
|
18
|
+
*/
|
|
19
|
+
exports.SECURITY = 'security';
|
|
20
|
+
exports.PRICE = 'price';
|
|
21
|
+
exports.PRICE_TYPE = 'price_type';
|
|
22
|
+
/** Returns every field-level violation on this price, or an empty array when it is valid. */
|
|
23
|
+
function validate(proto) {
|
|
24
|
+
var _a, _b, _c;
|
|
25
|
+
const p = proto !== null && proto !== void 0 ? proto : new price_pb_1.PriceProto();
|
|
26
|
+
const id = objectIdOf(p);
|
|
27
|
+
const out = [];
|
|
28
|
+
if (!((_b = (_a = p.getSecurity()) === null || _a === void 0 ? void 0 : _a.getUuid()) === null || _b === void 0 ? void 0 : _b.getRawUuid_asU8().length)) {
|
|
29
|
+
out.push((0, errors_1.violation)(exports.SECURITY, id, 'security with a non-empty uuid is required'));
|
|
30
|
+
}
|
|
31
|
+
if (!((_c = p.getPrice()) === null || _c === void 0 ? void 0 : _c.getArbitraryPrecisionValue())) {
|
|
32
|
+
out.push((0, errors_1.violation)(exports.PRICE, id, 'price is required'));
|
|
33
|
+
}
|
|
34
|
+
if (p.getPriceType() === price_type_pb_1.PriceTypeProto.UNKNOWN_PRICE_TYPE) {
|
|
35
|
+
out.push((0, errors_1.violation)(exports.PRICE_TYPE, id, 'price_type must not be UNKNOWN_PRICE_TYPE'));
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
exports.validate = validate;
|
|
40
|
+
/** Throws ModelValidationError carrying every violation if `validate` finds any. */
|
|
41
|
+
function requireValid(proto) {
|
|
42
|
+
const violations = validate(proto);
|
|
43
|
+
if (violations.length > 0)
|
|
44
|
+
throw new errors_1.ModelValidationError(violations);
|
|
45
|
+
}
|
|
46
|
+
exports.requireValid = requireValid;
|
|
47
|
+
/** The price's UUID, or undefined when it has none or it is not 16 bytes. */
|
|
48
|
+
function objectIdOf(proto) {
|
|
49
|
+
var _a;
|
|
50
|
+
const raw = (_a = proto.getUuid()) === null || _a === void 0 ? void 0 : _a.getRawUuid_asU8();
|
|
51
|
+
return raw && raw.length === 16 ? uuid_1.UUID.fromU8Array(raw) : undefined;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=price_rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"price_rules.js","sourceRoot":"","sources":["price_rules.ts"],"names":[],"mappings":";;;AAAA,wEAAuE;AACvE,kFAAgF;AAEhF,sCAA4D;AAC5D,wCAAqC;AAErC;;;;;;;;;;GAUG;AAEU,QAAA,QAAQ,GAAG,UAAU,CAAC;AACtB,QAAA,KAAK,GAAG,OAAO,CAAC;AAChB,QAAA,UAAU,GAAG,YAAY,CAAC;AAEvC,6FAA6F;AAC7F,SAAgB,QAAQ,CAAC,KAAoC;;IACzD,MAAM,CAAC,GAAG,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,IAAI,qBAAU,EAAE,CAAC;IACpC,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IACzB,MAAM,GAAG,GAA0B,EAAE,CAAC;IACtC,IAAI,CAAC,CAAA,MAAA,MAAA,CAAC,CAAC,WAAW,EAAE,0CAAE,OAAO,EAAE,0CAAE,eAAe,GAAG,MAAM,CAAA,EAAE;QACvD,GAAG,CAAC,IAAI,CAAC,IAAA,kBAAS,EAAC,gBAAQ,EAAE,EAAE,EAAE,4CAA4C,CAAC,CAAC,CAAC;KACnF;IACD,IAAI,CAAC,CAAA,MAAA,CAAC,CAAC,QAAQ,EAAE,0CAAE,0BAA0B,EAAE,CAAA,EAAE;QAC7C,GAAG,CAAC,IAAI,CAAC,IAAA,kBAAS,EAAC,aAAK,EAAE,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;KACvD;IACD,IAAI,CAAC,CAAC,YAAY,EAAE,KAAK,8BAAc,CAAC,kBAAkB,EAAE;QACxD,GAAG,CAAC,IAAI,CAAC,IAAA,kBAAS,EAAC,kBAAU,EAAE,EAAE,EAAE,2CAA2C,CAAC,CAAC,CAAC;KACpF;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAdD,4BAcC;AAED,oFAAoF;AACpF,SAAgB,YAAY,CAAC,KAAoC;IAC7D,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,6BAAoB,CAAC,UAAU,CAAC,CAAC;AAC1E,CAAC;AAHD,oCAGC;AAED,6EAA6E;AAC7E,SAAS,UAAU,CAAC,KAAiB;;IACjC,MAAM,GAAG,GAAG,MAAA,KAAK,CAAC,OAAO,EAAE,0CAAE,eAAe,EAAE,CAAC;IAC/C,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,WAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACxE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// LM-289: the shared price rule set. The same table runs in Java, Rust and Python.
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const price_pb_1 = require("../../../fintekkers/models/price/price_pb");
|
|
5
|
+
const price_type_pb_1 = require("../../../fintekkers/models/price/price_type_pb");
|
|
6
|
+
const security_pb_1 = require("../../../fintekkers/models/security/security_pb");
|
|
7
|
+
const decimal_value_pb_1 = require("../../../fintekkers/models/util/decimal_value_pb");
|
|
8
|
+
const uuid_pb_1 = require("../../../fintekkers/models/util/uuid_pb");
|
|
9
|
+
const errors_1 = require("../errors");
|
|
10
|
+
const uuid_1 = require("../utils/uuid");
|
|
11
|
+
const price_rules_1 = require("./price_rules");
|
|
12
|
+
const PRICE_ID = '6a1d2c3e-4b5f-4a6d-8e7f-9a0b1c2d3e4f';
|
|
13
|
+
const SECURITY_ID = '1f0e8a4c-58b1-4d0e-9a3c-7d2b6e5f4a10';
|
|
14
|
+
function decimal(value) {
|
|
15
|
+
return new decimal_value_pb_1.DecimalValueProto().setArbitraryPrecisionValue(value);
|
|
16
|
+
}
|
|
17
|
+
/** A price that passes every rule. */
|
|
18
|
+
function valid() {
|
|
19
|
+
return new price_pb_1.PriceProto()
|
|
20
|
+
.setUuid(new uuid_1.UUID(uuid_1.UUID.fromString(PRICE_ID)).toUUIDProto())
|
|
21
|
+
.setSecurity(new security_pb_1.SecurityProto().setUuid(new uuid_1.UUID(uuid_1.UUID.fromString(SECURITY_ID)).toUUIDProto()))
|
|
22
|
+
.setPrice(decimal('100.25'))
|
|
23
|
+
.setPriceType(price_type_pb_1.PriceTypeProto.PERCENTAGE);
|
|
24
|
+
}
|
|
25
|
+
/** `valid()` with `change` applied; the generated clear* setters return void. */
|
|
26
|
+
function validWith(change) {
|
|
27
|
+
const p = valid();
|
|
28
|
+
change(p);
|
|
29
|
+
return p;
|
|
30
|
+
}
|
|
31
|
+
function fields(proto) {
|
|
32
|
+
return (0, price_rules_1.validate)(proto).map(v => v.getField());
|
|
33
|
+
}
|
|
34
|
+
describe('price_rules.validate', () => {
|
|
35
|
+
test('security absent gives one violation on security', () => {
|
|
36
|
+
expect(fields(validWith(p => p.clearSecurity()))).toEqual(['security']);
|
|
37
|
+
});
|
|
38
|
+
test('empty security uuid gives one violation on security', () => {
|
|
39
|
+
expect(fields(valid().setSecurity(new security_pb_1.SecurityProto().setUuid(new uuid_pb_1.UUIDProto())))).toEqual(['security']);
|
|
40
|
+
});
|
|
41
|
+
test('security present but uuid unset gives one violation on security', () => {
|
|
42
|
+
expect(fields(valid().setSecurity(new security_pb_1.SecurityProto()))).toEqual(['security']);
|
|
43
|
+
});
|
|
44
|
+
test('price absent gives one violation on price', () => {
|
|
45
|
+
expect(fields(validWith(p => p.clearPrice()))).toEqual(['price']);
|
|
46
|
+
});
|
|
47
|
+
test('empty price gives one violation on price', () => {
|
|
48
|
+
expect(fields(valid().setPrice(decimal('')))).toEqual(['price']);
|
|
49
|
+
});
|
|
50
|
+
test('zero price is valid', () => {
|
|
51
|
+
expect(fields(valid().setPrice(decimal('0')))).toEqual([]);
|
|
52
|
+
});
|
|
53
|
+
test.each(['-1', 'abc'])('price value %s is not checked', value => {
|
|
54
|
+
expect(fields(valid().setPrice(decimal(value)))).toEqual([]);
|
|
55
|
+
});
|
|
56
|
+
test('UNKNOWN_PRICE_TYPE gives one violation on price_type', () => {
|
|
57
|
+
expect(fields(valid().setPriceType(price_type_pb_1.PriceTypeProto.UNKNOWN_PRICE_TYPE))).toEqual(['price_type']);
|
|
58
|
+
});
|
|
59
|
+
test('unrecognised price_type is valid', () => {
|
|
60
|
+
expect(fields(valid().setPriceType(99))).toEqual([]);
|
|
61
|
+
});
|
|
62
|
+
test('all three problems give three violations in field order', () => {
|
|
63
|
+
const proto = validWith(p => { p.clearSecurity(); p.clearPrice(); p.setPriceType(price_type_pb_1.PriceTypeProto.UNKNOWN_PRICE_TYPE); });
|
|
64
|
+
const violations = (0, price_rules_1.validate)(proto);
|
|
65
|
+
expect(violations.map(v => v.getField())).toEqual(['security', 'price', 'price_type']);
|
|
66
|
+
for (const v of violations) {
|
|
67
|
+
expect(uuid_1.UUID.fromU8Array(v.getObjectId().getRawUuid_asU8()).toString()).toBe(PRICE_ID);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
test('every known price type is valid', () => {
|
|
71
|
+
const values = Object.values(price_type_pb_1.PriceTypeProto).filter((v) => typeof v === 'number');
|
|
72
|
+
expect(values).toContain(price_type_pb_1.PriceTypeProto.PERCENTAGE);
|
|
73
|
+
for (const value of values) {
|
|
74
|
+
if (value === price_type_pb_1.PriceTypeProto.UNKNOWN_PRICE_TYPE)
|
|
75
|
+
continue;
|
|
76
|
+
expect(fields(valid().setPriceType(value))).toEqual([]);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
test('empty, null or undefined proto gives three violations without throwing', () => {
|
|
80
|
+
const expected = ['security', 'price', 'price_type'];
|
|
81
|
+
expect(fields(new price_pb_1.PriceProto())).toEqual(expected);
|
|
82
|
+
expect(fields(null)).toEqual(expected);
|
|
83
|
+
expect(fields(undefined)).toEqual(expected);
|
|
84
|
+
expect((0, price_rules_1.validate)(null)[0].hasObjectId()).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('price_rules.requireValid', () => {
|
|
88
|
+
test('throws the typed error carrying the violations', () => {
|
|
89
|
+
const proto = validWith(p => p.clearPrice());
|
|
90
|
+
let err;
|
|
91
|
+
try {
|
|
92
|
+
(0, price_rules_1.requireValid)(proto);
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
err = e;
|
|
96
|
+
}
|
|
97
|
+
expect(err).toBeInstanceOf(errors_1.ModelValidationError);
|
|
98
|
+
expect(err.violations.map(v => v.getField())).toEqual(['price']);
|
|
99
|
+
expect(() => (0, price_rules_1.requireValid)(valid())).not.toThrow();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
describe('the published (compiled) price_rules.js', () => {
|
|
103
|
+
// npm publish runs no tsc: the tracked .js is what consumers get.
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
105
|
+
const compiled = require('./price_rules.js');
|
|
106
|
+
test('all three problems give three violations in field order', () => {
|
|
107
|
+
const proto = validWith(p => { p.clearSecurity(); p.clearPrice(); p.setPriceType(price_type_pb_1.PriceTypeProto.UNKNOWN_PRICE_TYPE); });
|
|
108
|
+
const violations = compiled.validate(proto);
|
|
109
|
+
expect(violations.map(v => v.getField())).toEqual(['security', 'price', 'price_type']);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=price_rules.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"price_rules.test.js","sourceRoot":"","sources":["price_rules.test.ts"],"names":[],"mappings":";AAAA,mFAAmF;;AAEnF,wEAAuE;AACvE,kFAAgF;AAChF,iFAAgF;AAChF,uFAAqF;AACrF,qEAAoE;AACpE,sCAAiD;AACjD,wCAAqC;AACrC,+CAAuD;AAEvD,MAAM,QAAQ,GAAG,sCAAsC,CAAC;AACxD,MAAM,WAAW,GAAG,sCAAsC,CAAC;AAE3D,SAAS,OAAO,CAAC,KAAa;IAC1B,OAAO,IAAI,oCAAiB,EAAE,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC;AACrE,CAAC;AAED,sCAAsC;AACtC,SAAS,KAAK;IACV,OAAO,IAAI,qBAAU,EAAE;SAClB,OAAO,CAAC,IAAI,WAAI,CAAC,WAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;SAC1D,WAAW,CAAC,IAAI,2BAAa,EAAE,CAAC,OAAO,CAAC,IAAI,WAAI,CAAC,WAAI,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;SAC9F,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;SAC3B,YAAY,CAAC,8BAAc,CAAC,UAAU,CAAC,CAAC;AACjD,CAAC;AAED,iFAAiF;AACjF,SAAS,SAAS,CAAC,MAA+B;IAC9C,MAAM,CAAC,GAAG,KAAK,EAAE,CAAC;IAClB,MAAM,CAAC,CAAC,CAAC,CAAC;IACV,OAAO,CAAC,CAAC;AACb,CAAC;AAED,SAAS,MAAM,CAAC,KAAoC;IAChD,OAAO,IAAA,sBAAQ,EAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;AAClD,CAAC;AAED,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IAClC,IAAI,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,WAAW,CAAC,IAAI,2BAAa,EAAE,CAAC,OAAO,CAAC,IAAI,mBAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAC5G,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,WAAW,CAAC,IAAI,2BAAa,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,+BAA+B,EAAE,KAAK,CAAC,EAAE;QAC9D,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,YAAY,CAAC,8BAAc,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,YAAY,CAAC,EAAoB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,8BAAc,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxH,MAAM,UAAU,GAAG,IAAA,sBAAQ,EAAC,KAAK,CAAC,CAAC;QACnC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;QACvF,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE;YACxB,MAAM,CAAC,WAAI,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,EAAG,CAAC,eAAe,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;SAC1F;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,8BAAc,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAuB,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;QACvG,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,8BAAc,CAAC,UAAU,CAAC,CAAC;QACpD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;YACxB,IAAI,KAAK,KAAK,8BAAc,CAAC,kBAAkB;gBAAE,SAAS;YAC1D,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;SAC3D;IACL,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,QAAQ,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;QACrD,MAAM,CAAC,MAAM,CAAC,IAAI,qBAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAA,sBAAQ,EAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACtC,IAAI,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;QAC7C,IAAI,GAAY,CAAC;QACjB,IAAI;YACA,IAAA,0BAAY,EAAC,KAAK,CAAC,CAAC;SACvB;QAAC,OAAO,CAAC,EAAE;YACR,GAAG,GAAG,CAAC,CAAC;SACX;QACD,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,6BAAoB,CAAC,CAAC;QACjD,MAAM,CAAE,GAA4B,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3F,MAAM,CAAC,GAAG,EAAE,CAAC,IAAA,0BAAY,EAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACtD,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACrD,kEAAkE;IAClE,8DAA8D;IAC9D,MAAM,QAAQ,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAE7C,IAAI,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,8BAAc,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxH,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAA6B,CAAC;QACxE,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;IAC3F,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// LM-289: the shared price rule set. The same table runs in Java, Rust and Python.
|
|
2
|
+
|
|
3
|
+
import { PriceProto } from '../../../fintekkers/models/price/price_pb';
|
|
4
|
+
import { PriceTypeProto } from '../../../fintekkers/models/price/price_type_pb';
|
|
5
|
+
import { SecurityProto } from '../../../fintekkers/models/security/security_pb';
|
|
6
|
+
import { DecimalValueProto } from '../../../fintekkers/models/util/decimal_value_pb';
|
|
7
|
+
import { UUIDProto } from '../../../fintekkers/models/util/uuid_pb';
|
|
8
|
+
import { ModelValidationError } from '../errors';
|
|
9
|
+
import { UUID } from '../utils/uuid';
|
|
10
|
+
import { requireValid, validate } from './price_rules';
|
|
11
|
+
|
|
12
|
+
const PRICE_ID = '6a1d2c3e-4b5f-4a6d-8e7f-9a0b1c2d3e4f';
|
|
13
|
+
const SECURITY_ID = '1f0e8a4c-58b1-4d0e-9a3c-7d2b6e5f4a10';
|
|
14
|
+
|
|
15
|
+
function decimal(value: string): DecimalValueProto {
|
|
16
|
+
return new DecimalValueProto().setArbitraryPrecisionValue(value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** A price that passes every rule. */
|
|
20
|
+
function valid(): PriceProto {
|
|
21
|
+
return new PriceProto()
|
|
22
|
+
.setUuid(new UUID(UUID.fromString(PRICE_ID)).toUUIDProto())
|
|
23
|
+
.setSecurity(new SecurityProto().setUuid(new UUID(UUID.fromString(SECURITY_ID)).toUUIDProto()))
|
|
24
|
+
.setPrice(decimal('100.25'))
|
|
25
|
+
.setPriceType(PriceTypeProto.PERCENTAGE);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** `valid()` with `change` applied; the generated clear* setters return void. */
|
|
29
|
+
function validWith(change: (p: PriceProto) => void): PriceProto {
|
|
30
|
+
const p = valid();
|
|
31
|
+
change(p);
|
|
32
|
+
return p;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function fields(proto: PriceProto | null | undefined): string[] {
|
|
36
|
+
return validate(proto).map(v => v.getField());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe('price_rules.validate', () => {
|
|
40
|
+
test('security absent gives one violation on security', () => {
|
|
41
|
+
expect(fields(validWith(p => p.clearSecurity()))).toEqual(['security']);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('empty security uuid gives one violation on security', () => {
|
|
45
|
+
expect(fields(valid().setSecurity(new SecurityProto().setUuid(new UUIDProto())))).toEqual(['security']);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('security present but uuid unset gives one violation on security', () => {
|
|
49
|
+
expect(fields(valid().setSecurity(new SecurityProto()))).toEqual(['security']);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('price absent gives one violation on price', () => {
|
|
53
|
+
expect(fields(validWith(p => p.clearPrice()))).toEqual(['price']);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('empty price gives one violation on price', () => {
|
|
57
|
+
expect(fields(valid().setPrice(decimal('')))).toEqual(['price']);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('zero price is valid', () => {
|
|
61
|
+
expect(fields(valid().setPrice(decimal('0')))).toEqual([]);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test.each(['-1', 'abc'])('price value %s is not checked', value => {
|
|
65
|
+
expect(fields(valid().setPrice(decimal(value)))).toEqual([]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('UNKNOWN_PRICE_TYPE gives one violation on price_type', () => {
|
|
69
|
+
expect(fields(valid().setPriceType(PriceTypeProto.UNKNOWN_PRICE_TYPE))).toEqual(['price_type']);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('unrecognised price_type is valid', () => {
|
|
73
|
+
expect(fields(valid().setPriceType(99 as PriceTypeProto))).toEqual([]);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('all three problems give three violations in field order', () => {
|
|
77
|
+
const proto = validWith(p => { p.clearSecurity(); p.clearPrice(); p.setPriceType(PriceTypeProto.UNKNOWN_PRICE_TYPE); });
|
|
78
|
+
const violations = validate(proto);
|
|
79
|
+
expect(violations.map(v => v.getField())).toEqual(['security', 'price', 'price_type']);
|
|
80
|
+
for (const v of violations) {
|
|
81
|
+
expect(UUID.fromU8Array(v.getObjectId()!.getRawUuid_asU8()).toString()).toBe(PRICE_ID);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('every known price type is valid', () => {
|
|
86
|
+
const values = Object.values(PriceTypeProto).filter((v): v is PriceTypeProto => typeof v === 'number');
|
|
87
|
+
expect(values).toContain(PriceTypeProto.PERCENTAGE);
|
|
88
|
+
for (const value of values) {
|
|
89
|
+
if (value === PriceTypeProto.UNKNOWN_PRICE_TYPE) continue;
|
|
90
|
+
expect(fields(valid().setPriceType(value))).toEqual([]);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('empty, null or undefined proto gives three violations without throwing', () => {
|
|
95
|
+
const expected = ['security', 'price', 'price_type'];
|
|
96
|
+
expect(fields(new PriceProto())).toEqual(expected);
|
|
97
|
+
expect(fields(null)).toEqual(expected);
|
|
98
|
+
expect(fields(undefined)).toEqual(expected);
|
|
99
|
+
expect(validate(null)[0].hasObjectId()).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('price_rules.requireValid', () => {
|
|
104
|
+
test('throws the typed error carrying the violations', () => {
|
|
105
|
+
const proto = validWith(p => p.clearPrice());
|
|
106
|
+
let err: unknown;
|
|
107
|
+
try {
|
|
108
|
+
requireValid(proto);
|
|
109
|
+
} catch (e) {
|
|
110
|
+
err = e;
|
|
111
|
+
}
|
|
112
|
+
expect(err).toBeInstanceOf(ModelValidationError);
|
|
113
|
+
expect((err as ModelValidationError).violations.map(v => v.getField())).toEqual(['price']);
|
|
114
|
+
expect(() => requireValid(valid())).not.toThrow();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('the published (compiled) price_rules.js', () => {
|
|
119
|
+
// npm publish runs no tsc: the tracked .js is what consumers get.
|
|
120
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
121
|
+
const compiled = require('./price_rules.js');
|
|
122
|
+
|
|
123
|
+
test('all three problems give three violations in field order', () => {
|
|
124
|
+
const proto = validWith(p => { p.clearSecurity(); p.clearPrice(); p.setPriceType(PriceTypeProto.UNKNOWN_PRICE_TYPE); });
|
|
125
|
+
const violations = compiled.validate(proto) as { getField(): string }[];
|
|
126
|
+
expect(violations.map(v => v.getField())).toEqual(['security', 'price', 'price_type']);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { PriceProto } from '../../../fintekkers/models/price/price_pb';
|
|
2
|
+
import { PriceTypeProto } from '../../../fintekkers/models/price/price_type_pb';
|
|
3
|
+
import { FieldViolationProto } from '../../../fintekkers/requests/util/errors/field_violation_pb';
|
|
4
|
+
import { ModelValidationError, violation } from '../errors';
|
|
5
|
+
import { UUID } from '../utils/uuid';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Price input rules (LM-289): JS counterpart of Java's PriceRules
|
|
9
|
+
* (docs/adr/typed-input-errors.md). Pure: proto in, violations out.
|
|
10
|
+
*
|
|
11
|
+
* Three rules, checked in this order: `security` has a non-empty UUID,
|
|
12
|
+
* `price` has a non-empty arbitrary_precision_value (zero is valid, see the
|
|
13
|
+
* PriceProto comment), and `price_type` is not UNKNOWN_PRICE_TYPE. The price
|
|
14
|
+
* value is not parsed. Never throws on bad or unset input; null or undefined
|
|
15
|
+
* is checked as an empty PriceProto. Unlike security_rules, a link price is
|
|
16
|
+
* not skipped: hydrate it first. The Price wrapper does not call these rules.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export const SECURITY = 'security';
|
|
20
|
+
export const PRICE = 'price';
|
|
21
|
+
export const PRICE_TYPE = 'price_type';
|
|
22
|
+
|
|
23
|
+
/** Returns every field-level violation on this price, or an empty array when it is valid. */
|
|
24
|
+
export function validate(proto: PriceProto | null | undefined): FieldViolationProto[] {
|
|
25
|
+
const p = proto ?? new PriceProto();
|
|
26
|
+
const id = objectIdOf(p);
|
|
27
|
+
const out: FieldViolationProto[] = [];
|
|
28
|
+
if (!p.getSecurity()?.getUuid()?.getRawUuid_asU8().length) {
|
|
29
|
+
out.push(violation(SECURITY, id, 'security with a non-empty uuid is required'));
|
|
30
|
+
}
|
|
31
|
+
if (!p.getPrice()?.getArbitraryPrecisionValue()) {
|
|
32
|
+
out.push(violation(PRICE, id, 'price is required'));
|
|
33
|
+
}
|
|
34
|
+
if (p.getPriceType() === PriceTypeProto.UNKNOWN_PRICE_TYPE) {
|
|
35
|
+
out.push(violation(PRICE_TYPE, id, 'price_type must not be UNKNOWN_PRICE_TYPE'));
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Throws ModelValidationError carrying every violation if `validate` finds any. */
|
|
41
|
+
export function requireValid(proto: PriceProto | null | undefined): void {
|
|
42
|
+
const violations = validate(proto);
|
|
43
|
+
if (violations.length > 0) throw new ModelValidationError(violations);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The price's UUID, or undefined when it has none or it is not 16 bytes. */
|
|
47
|
+
function objectIdOf(proto: PriceProto): UUID | undefined {
|
|
48
|
+
const raw = proto.getUuid()?.getRawUuid_asU8();
|
|
49
|
+
return raw && raw.length === 16 ? UUID.fromU8Array(raw) : undefined;
|
|
50
|
+
}
|
|
@@ -33,6 +33,9 @@ const grpc = __importStar(require("@grpc/grpc-js"));
|
|
|
33
33
|
const requestcontext_1 = __importStar(require("./requestcontext"));
|
|
34
34
|
const savedApiUrl = process.env.API_URL;
|
|
35
35
|
const savedApiKey = process.env.API_KEY;
|
|
36
|
+
// Variables the lookup reads before API_URL; the host may set them.
|
|
37
|
+
const SHADOWING = ['BROKER_HOST', 'LEDGER_SERVICE_HOST', 'LEDGER_SERVICE_PORT'];
|
|
38
|
+
const savedShadowing = SHADOWING.map((k) => process.env[k]);
|
|
36
39
|
function restoreEnv(name, value) {
|
|
37
40
|
if (value === undefined) {
|
|
38
41
|
delete process.env[name];
|
|
@@ -44,10 +47,12 @@ function restoreEnv(name, value) {
|
|
|
44
47
|
beforeEach(() => {
|
|
45
48
|
restoreEnv('API_URL', undefined);
|
|
46
49
|
restoreEnv('API_KEY', undefined);
|
|
50
|
+
SHADOWING.forEach((k) => restoreEnv(k, undefined));
|
|
47
51
|
});
|
|
48
52
|
afterEach(() => {
|
|
49
53
|
restoreEnv('API_URL', savedApiUrl);
|
|
50
54
|
restoreEnv('API_KEY', savedApiKey);
|
|
55
|
+
SHADOWING.forEach((k, i) => restoreEnv(k, savedShadowing[i]));
|
|
51
56
|
jest.restoreAllMocks();
|
|
52
57
|
});
|
|
53
58
|
describe('EnvConfig.apiURL', () => {
|
|
@@ -59,8 +64,8 @@ describe('EnvConfig.apiURL', () => {
|
|
|
59
64
|
process.env.API_URL = 'localhost';
|
|
60
65
|
expect(requestcontext_1.default.apiURL).toBe('localhost:8082');
|
|
61
66
|
});
|
|
62
|
-
test('defaults to
|
|
63
|
-
expect(requestcontext_1.default.apiURL).toBe('
|
|
67
|
+
test('defaults to localhost:8082 when API_URL is unset', () => {
|
|
68
|
+
expect(requestcontext_1.default.apiURL).toBe('localhost:8082');
|
|
64
69
|
});
|
|
65
70
|
});
|
|
66
71
|
describe('EnvConfig.apiKey', () => {
|
|
@@ -82,7 +87,15 @@ describe('EnvConfig.apiCredentials', () => {
|
|
|
82
87
|
expect(insecure).toHaveBeenCalledTimes(1);
|
|
83
88
|
expect(ssl).not.toHaveBeenCalled();
|
|
84
89
|
});
|
|
85
|
-
test('uses
|
|
90
|
+
test('uses insecure credentials when API_URL is unset (localhost:8082)', () => {
|
|
91
|
+
const insecure = jest.spyOn(grpc.credentials, 'createInsecure');
|
|
92
|
+
const ssl = jest.spyOn(grpc.credentials, 'createSsl');
|
|
93
|
+
requestcontext_1.default.apiCredentials;
|
|
94
|
+
expect(insecure).toHaveBeenCalledTimes(1);
|
|
95
|
+
expect(ssl).not.toHaveBeenCalled();
|
|
96
|
+
});
|
|
97
|
+
test('uses SSL credentials for a remote API_URL', () => {
|
|
98
|
+
process.env.API_URL = 'myhost.example.com';
|
|
86
99
|
const insecure = jest.spyOn(grpc.credentials, 'createInsecure');
|
|
87
100
|
const ssl = jest.spyOn(grpc.credentials, 'createSsl');
|
|
88
101
|
requestcontext_1.default.apiCredentials;
|
|
@@ -92,13 +105,14 @@ describe('EnvConfig.apiCredentials', () => {
|
|
|
92
105
|
});
|
|
93
106
|
describe('EnvConfig.getAuthenticatedCredentials', () => {
|
|
94
107
|
test('metadata generator injects x-api-key', () => {
|
|
108
|
+
process.env.API_URL = 'myhost.example.com';
|
|
95
109
|
const generatorSpy = jest.spyOn(grpc.credentials, 'createFromMetadataGenerator');
|
|
96
110
|
const creds = requestcontext_1.default.getAuthenticatedCredentials('k');
|
|
97
111
|
expect(creds).toBeInstanceOf(grpc.ChannelCredentials);
|
|
98
112
|
expect(generatorSpy).toHaveBeenCalledTimes(1);
|
|
99
113
|
const generator = generatorSpy.mock.calls[0][0];
|
|
100
114
|
const callback = jest.fn();
|
|
101
|
-
generator({ service_url: 'https://
|
|
115
|
+
generator({ service_url: 'https://myhost.example.com:8082' }, callback);
|
|
102
116
|
expect(callback).toHaveBeenCalledTimes(1);
|
|
103
117
|
const [err, metadata] = callback.mock.calls[0];
|
|
104
118
|
expect(err).toBeNull();
|
|
@@ -127,6 +141,7 @@ describe('EnvConfig.getAuthenticatedCredentials', () => {
|
|
|
127
141
|
jest.spyOn(console, 'error').mockImplementation(() => { }),
|
|
128
142
|
];
|
|
129
143
|
const generatorSpy = jest.spyOn(grpc.credentials, 'createFromMetadataGenerator');
|
|
144
|
+
process.env.API_URL = 'myhost.example.com';
|
|
130
145
|
requestcontext_1.default.getAuthenticatedCredentials(secret);
|
|
131
146
|
generatorSpy.mock.calls[0][0]({ service_url: '' }, () => { });
|
|
132
147
|
process.env.API_URL = 'localhost:80';
|
|
@@ -148,8 +163,8 @@ describe('EnvConfig.getAuthenticatedClientOptions', () => {
|
|
|
148
163
|
expect(ssl).not.toHaveBeenCalled();
|
|
149
164
|
expect(interceptors).toHaveLength(1);
|
|
150
165
|
});
|
|
151
|
-
test('uses SSL channel for
|
|
152
|
-
process.env.API_URL = '
|
|
166
|
+
test('uses SSL channel for a remote host', () => {
|
|
167
|
+
process.env.API_URL = 'myhost.example.com';
|
|
153
168
|
const insecure = jest.spyOn(grpc.credentials, 'createInsecure');
|
|
154
169
|
const ssl = jest.spyOn(grpc.credentials, 'createSsl');
|
|
155
170
|
const { interceptors } = requestcontext_1.default.getAuthenticatedClientOptions('k');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requestcontext.auth.test.js","sourceRoot":"","sources":["requestcontext.auth.test.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,oDAAsC;AACtC,mEAAuE;AAEvE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;AACxC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"requestcontext.auth.test.js","sourceRoot":"","sources":["requestcontext.auth.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,6 +10,9 @@ import EnvConfig, { InsecureChannelAuthError } from './requestcontext';
|
|
|
10
10
|
|
|
11
11
|
const savedApiUrl = process.env.API_URL;
|
|
12
12
|
const savedApiKey = process.env.API_KEY;
|
|
13
|
+
// Variables the lookup reads before API_URL; the host may set them.
|
|
14
|
+
const SHADOWING = ['BROKER_HOST', 'LEDGER_SERVICE_HOST', 'LEDGER_SERVICE_PORT'];
|
|
15
|
+
const savedShadowing = SHADOWING.map((k) => process.env[k]);
|
|
13
16
|
|
|
14
17
|
function restoreEnv(name: string, value: string | undefined) {
|
|
15
18
|
if (value === undefined) {
|
|
@@ -22,11 +25,13 @@ function restoreEnv(name: string, value: string | undefined) {
|
|
|
22
25
|
beforeEach(() => {
|
|
23
26
|
restoreEnv('API_URL', undefined);
|
|
24
27
|
restoreEnv('API_KEY', undefined);
|
|
28
|
+
SHADOWING.forEach((k) => restoreEnv(k, undefined));
|
|
25
29
|
});
|
|
26
30
|
|
|
27
31
|
afterEach(() => {
|
|
28
32
|
restoreEnv('API_URL', savedApiUrl);
|
|
29
33
|
restoreEnv('API_KEY', savedApiKey);
|
|
34
|
+
SHADOWING.forEach((k, i) => restoreEnv(k, savedShadowing[i]));
|
|
30
35
|
jest.restoreAllMocks();
|
|
31
36
|
});
|
|
32
37
|
|
|
@@ -41,8 +46,8 @@ describe('EnvConfig.apiURL', () => {
|
|
|
41
46
|
expect(EnvConfig.apiURL).toBe('localhost:8082');
|
|
42
47
|
});
|
|
43
48
|
|
|
44
|
-
test('defaults to
|
|
45
|
-
expect(EnvConfig.apiURL).toBe('
|
|
49
|
+
test('defaults to localhost:8082 when API_URL is unset', () => {
|
|
50
|
+
expect(EnvConfig.apiURL).toBe('localhost:8082');
|
|
46
51
|
});
|
|
47
52
|
});
|
|
48
53
|
|
|
@@ -70,7 +75,18 @@ describe('EnvConfig.apiCredentials', () => {
|
|
|
70
75
|
expect(ssl).not.toHaveBeenCalled();
|
|
71
76
|
});
|
|
72
77
|
|
|
73
|
-
test('uses
|
|
78
|
+
test('uses insecure credentials when API_URL is unset (localhost:8082)', () => {
|
|
79
|
+
const insecure = jest.spyOn(grpc.credentials, 'createInsecure');
|
|
80
|
+
const ssl = jest.spyOn(grpc.credentials, 'createSsl');
|
|
81
|
+
|
|
82
|
+
EnvConfig.apiCredentials;
|
|
83
|
+
|
|
84
|
+
expect(insecure).toHaveBeenCalledTimes(1);
|
|
85
|
+
expect(ssl).not.toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('uses SSL credentials for a remote API_URL', () => {
|
|
89
|
+
process.env.API_URL = 'myhost.example.com';
|
|
74
90
|
const insecure = jest.spyOn(grpc.credentials, 'createInsecure');
|
|
75
91
|
const ssl = jest.spyOn(grpc.credentials, 'createSsl');
|
|
76
92
|
|
|
@@ -83,6 +99,7 @@ describe('EnvConfig.apiCredentials', () => {
|
|
|
83
99
|
|
|
84
100
|
describe('EnvConfig.getAuthenticatedCredentials', () => {
|
|
85
101
|
test('metadata generator injects x-api-key', () => {
|
|
102
|
+
process.env.API_URL = 'myhost.example.com';
|
|
86
103
|
const generatorSpy = jest.spyOn(grpc.credentials, 'createFromMetadataGenerator');
|
|
87
104
|
|
|
88
105
|
const creds = EnvConfig.getAuthenticatedCredentials('k');
|
|
@@ -92,7 +109,7 @@ describe('EnvConfig.getAuthenticatedCredentials', () => {
|
|
|
92
109
|
|
|
93
110
|
const generator = generatorSpy.mock.calls[0][0];
|
|
94
111
|
const callback = jest.fn();
|
|
95
|
-
generator({ service_url: 'https://
|
|
112
|
+
generator({ service_url: 'https://myhost.example.com:8082' }, callback);
|
|
96
113
|
|
|
97
114
|
expect(callback).toHaveBeenCalledTimes(1);
|
|
98
115
|
const [err, metadata] = callback.mock.calls[0];
|
|
@@ -129,6 +146,7 @@ describe('EnvConfig.getAuthenticatedCredentials', () => {
|
|
|
129
146
|
];
|
|
130
147
|
const generatorSpy = jest.spyOn(grpc.credentials, 'createFromMetadataGenerator');
|
|
131
148
|
|
|
149
|
+
process.env.API_URL = 'myhost.example.com';
|
|
132
150
|
EnvConfig.getAuthenticatedCredentials(secret);
|
|
133
151
|
generatorSpy.mock.calls[0][0]({ service_url: '' }, () => {});
|
|
134
152
|
process.env.API_URL = 'localhost:80';
|
|
@@ -155,8 +173,8 @@ describe('EnvConfig.getAuthenticatedClientOptions', () => {
|
|
|
155
173
|
expect(interceptors).toHaveLength(1);
|
|
156
174
|
});
|
|
157
175
|
|
|
158
|
-
test('uses SSL channel for
|
|
159
|
-
process.env.API_URL = '
|
|
176
|
+
test('uses SSL channel for a remote host', () => {
|
|
177
|
+
process.env.API_URL = 'myhost.example.com';
|
|
160
178
|
const insecure = jest.spyOn(grpc.credentials, 'createInsecure');
|
|
161
179
|
const ssl = jest.spyOn(grpc.credentials, 'createSsl');
|
|
162
180
|
|