@fintekkers/ledger-models 0.4.9 → 0.4.11
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/hydrate.test.d.ts +1 -0
- package/node/wrappers/models/hydrate.test.js +204 -0
- package/node/wrappers/models/hydrate.test.js.map +1 -0
- package/node/wrappers/models/hydrate.test.ts +214 -0
- package/node/wrappers/models/lazy-hydrate-race.test.d.ts +1 -0
- package/node/wrappers/models/lazy-hydrate-race.test.js +153 -0
- package/node/wrappers/models/lazy-hydrate-race.test.js.map +1 -0
- package/node/wrappers/models/lazy-hydrate-race.test.ts +144 -0
- package/node/wrappers/models/lazy-hydrate.bench.test.d.ts +1 -0
- package/node/wrappers/models/lazy-hydrate.bench.test.js +121 -0
- package/node/wrappers/models/lazy-hydrate.bench.test.js.map +1 -0
- package/node/wrappers/models/lazy-hydrate.bench.test.ts +103 -0
- package/node/wrappers/models/portfolio/portfolio.d.ts +18 -0
- package/node/wrappers/models/portfolio/portfolio.js +93 -1
- package/node/wrappers/models/portfolio/portfolio.js.map +1 -1
- package/node/wrappers/models/portfolio/portfolio.ts +58 -1
- package/node/wrappers/models/portfolio-price-transaction.lazy-hydrate.test.d.ts +1 -0
- package/node/wrappers/models/portfolio-price-transaction.lazy-hydrate.test.js +158 -0
- package/node/wrappers/models/portfolio-price-transaction.lazy-hydrate.test.js.map +1 -0
- package/node/wrappers/models/portfolio-price-transaction.lazy-hydrate.test.ts +153 -0
- package/node/wrappers/models/price/Price.d.ts +5 -0
- package/node/wrappers/models/price/Price.js +48 -0
- package/node/wrappers/models/price/Price.js.map +1 -1
- package/node/wrappers/models/price/Price.ts +26 -0
- package/node/wrappers/models/security/identifier-validation.test.d.ts +1 -0
- package/node/wrappers/models/security/identifier-validation.test.js +104 -0
- package/node/wrappers/models/security/identifier-validation.test.js.map +1 -0
- package/node/wrappers/models/security/identifier-validation.test.ts +133 -0
- package/node/wrappers/models/security/identifier.d.ts +26 -0
- package/node/wrappers/models/security/identifier.js +54 -1
- package/node/wrappers/models/security/identifier.js.map +1 -1
- package/node/wrappers/models/security/identifier.test.js +6 -9
- package/node/wrappers/models/security/identifier.test.js.map +1 -1
- package/node/wrappers/models/security/identifier.test.ts +6 -9
- package/node/wrappers/models/security/identifier.ts +58 -0
- package/node/wrappers/models/security/security.d.ts +23 -5
- package/node/wrappers/models/security/security.js +53 -6
- package/node/wrappers/models/security/security.js.map +1 -1
- package/node/wrappers/models/security/security.ts +38 -6
- package/node/wrappers/models/transaction/transaction.d.ts +18 -0
- package/node/wrappers/models/transaction/transaction.js +98 -0
- package/node/wrappers/models/transaction/transaction.js.map +1 -1
- package/node/wrappers/models/transaction/transaction.ts +65 -0
- package/node/wrappers/services/portfolio-service/PortfolioService.js +35 -0
- package/node/wrappers/services/portfolio-service/PortfolioService.js.map +1 -1
- package/node/wrappers/services/portfolio-service/PortfolioService.ts +14 -2
- package/node/wrappers/services/price-service/PriceService.js +10 -0
- package/node/wrappers/services/price-service/PriceService.js.map +1 -1
- package/node/wrappers/services/price-service/PriceService.ts +12 -2
- package/node/wrappers/services/security-service/SecurityService.js +23 -0
- package/node/wrappers/services/security-service/SecurityService.js.map +1 -1
- package/node/wrappers/services/security-service/SecurityService.ts +27 -2
- package/node/wrappers/services/security.identifier-guard.test.d.ts +1 -0
- package/node/wrappers/services/security.identifier-guard.test.js +63 -0
- package/node/wrappers/services/security.identifier-guard.test.js.map +1 -0
- package/node/wrappers/services/security.identifier-guard.test.ts +70 -0
- package/node/wrappers/services/service-client-writethrough.test.d.ts +1 -0
- package/node/wrappers/services/service-client-writethrough.test.js +147 -0
- package/node/wrappers/services/service-client-writethrough.test.js.map +1 -0
- package/node/wrappers/services/service-client-writethrough.test.ts +141 -0
- package/node/wrappers/services/transaction-service/TransactionService.js +36 -0
- package/node/wrappers/services/transaction-service/TransactionService.js.map +1 -1
- package/node/wrappers/services/transaction-service/TransactionService.ts +13 -0
- package/node/wrappers/util/link-cache.d.ts +13 -6
- package/node/wrappers/util/link-cache.js +51 -15
- package/node/wrappers/util/link-cache.js.map +1 -1
- package/node/wrappers/util/link-cache.ts +51 -17
- package/node/wrappers/util/link-resolver.d.ts +39 -31
- package/node/wrappers/util/link-resolver.js +157 -97
- package/node/wrappers/util/link-resolver.js.map +1 -1
- package/node/wrappers/util/link-resolver.test.js +88 -2
- package/node/wrappers/util/link-resolver.test.js.map +1 -1
- package/node/wrappers/util/link-resolver.test.ts +76 -2
- package/node/wrappers/util/link-resolver.ts +143 -124
- package/package.json +1 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// Client-side identifier guard tests (FinTekkers/second-brain#347).
|
|
2
|
+
// Pins behaviour of the consumer-side reject so callers fail fast on the
|
|
3
|
+
// client before the gRPC round-trip, mirroring the server's
|
|
4
|
+
// SecurityAPIGRPCImpl.validateCreateRequest UNKNOWN_IDENTIFIER_TYPE check.
|
|
5
|
+
|
|
6
|
+
import { IdentifierProto } from '../../../fintekkers/models/security/identifier/identifier_pb';
|
|
7
|
+
import { IdentifierTypeProto } from '../../../fintekkers/models/security/identifier/identifier_type_pb';
|
|
8
|
+
import { SecurityProto } from '../../../fintekkers/models/security/security_pb';
|
|
9
|
+
import {
|
|
10
|
+
IdentifierValidationError,
|
|
11
|
+
validateIdentifierProto,
|
|
12
|
+
validateIdentifiersForCreate,
|
|
13
|
+
} from './identifier';
|
|
14
|
+
|
|
15
|
+
function makeIdentifier(type: IdentifierTypeProto, value: string): IdentifierProto {
|
|
16
|
+
const p = new IdentifierProto();
|
|
17
|
+
p.setIdentifierType(type);
|
|
18
|
+
p.setIdentifierValue(value);
|
|
19
|
+
return p;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ---------- validateIdentifierProto ----------
|
|
23
|
+
|
|
24
|
+
describe('validateIdentifierProto', () => {
|
|
25
|
+
test('rejects UNKNOWN_IDENTIFIER_TYPE with a helpful message', () => {
|
|
26
|
+
const bad = makeIdentifier(
|
|
27
|
+
IdentifierTypeProto.UNKNOWN_IDENTIFIER_TYPE,
|
|
28
|
+
'some-uuid-hex'
|
|
29
|
+
);
|
|
30
|
+
let err: Error | undefined;
|
|
31
|
+
try {
|
|
32
|
+
validateIdentifierProto(bad);
|
|
33
|
+
} catch (e) {
|
|
34
|
+
err = e as Error;
|
|
35
|
+
}
|
|
36
|
+
expect(err).toBeInstanceOf(IdentifierValidationError);
|
|
37
|
+
expect(err!.message).toMatch(/UNKNOWN_IDENTIFIER_TYPE/);
|
|
38
|
+
// Surfaces the valid alternatives so the caller can fix the typo
|
|
39
|
+
expect(err!.message).toMatch(/EXCH_TICKER/);
|
|
40
|
+
expect(err!.message).toMatch(/#347/);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('rejects a default-constructed identifier (type=0, empty value)', () => {
|
|
44
|
+
const bad = new IdentifierProto();
|
|
45
|
+
expect(() => validateIdentifierProto(bad)).toThrow(IdentifierValidationError);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('rejects empty identifier_value with the type name in the message', () => {
|
|
49
|
+
const bad = makeIdentifier(IdentifierTypeProto.EXCH_TICKER, '');
|
|
50
|
+
let err: Error | undefined;
|
|
51
|
+
try {
|
|
52
|
+
validateIdentifierProto(bad);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
err = e as Error;
|
|
55
|
+
}
|
|
56
|
+
expect(err).toBeInstanceOf(IdentifierValidationError);
|
|
57
|
+
expect(err!.message).toMatch(/empty/);
|
|
58
|
+
expect(err!.message).toMatch(/EXCH_TICKER/);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('rejects whitespace-only identifier_value', () => {
|
|
62
|
+
const bad = makeIdentifier(IdentifierTypeProto.CUSIP, ' ');
|
|
63
|
+
expect(() => validateIdentifierProto(bad)).toThrow(IdentifierValidationError);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test.each([
|
|
67
|
+
['EXCH_TICKER', IdentifierTypeProto.EXCH_TICKER, 'AAPL'],
|
|
68
|
+
['ISIN', IdentifierTypeProto.ISIN, 'US0378331005'],
|
|
69
|
+
['CUSIP', IdentifierTypeProto.CUSIP, '037833100'],
|
|
70
|
+
['FIGI', IdentifierTypeProto.FIGI, 'BBG000B9XRY4'],
|
|
71
|
+
['OSI', IdentifierTypeProto.OSI, 'AAPL 250620C00150000'],
|
|
72
|
+
['SERIES_ID', IdentifierTypeProto.SERIES_ID, 'GS10'],
|
|
73
|
+
['INDEX_NAME', IdentifierTypeProto.INDEX_NAME, 'SPX'],
|
|
74
|
+
['CASH', IdentifierTypeProto.CASH, 'USD'],
|
|
75
|
+
])('accepts every real identifier type (%s)', (_name, type, value) => {
|
|
76
|
+
const good = makeIdentifier(type, value as string);
|
|
77
|
+
expect(() => validateIdentifierProto(good)).not.toThrow();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('IdentifierValidationError is an Error', () => {
|
|
81
|
+
// Catch-by-Error still works for callers that don't import the
|
|
82
|
+
// specific subclass.
|
|
83
|
+
const err = new IdentifierValidationError('x');
|
|
84
|
+
expect(err).toBeInstanceOf(Error);
|
|
85
|
+
expect(err.name).toBe('IdentifierValidationError');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// ---------- validateIdentifiersForCreate ----------
|
|
90
|
+
|
|
91
|
+
describe('validateIdentifiersForCreate', () => {
|
|
92
|
+
test('passes when every identifier on the SecurityProto is well-typed', () => {
|
|
93
|
+
const security = new SecurityProto();
|
|
94
|
+
security.addIdentifiers(
|
|
95
|
+
makeIdentifier(IdentifierTypeProto.EXCH_TICKER, 'AAPL')
|
|
96
|
+
);
|
|
97
|
+
security.addIdentifiers(
|
|
98
|
+
makeIdentifier(IdentifierTypeProto.ISIN, 'US0378331005')
|
|
99
|
+
);
|
|
100
|
+
expect(() => validateIdentifiersForCreate(security)).not.toThrow();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('rejects when any identifier in the list is UNKNOWN_IDENTIFIER_TYPE', () => {
|
|
104
|
+
const security = new SecurityProto();
|
|
105
|
+
security.addIdentifiers(
|
|
106
|
+
makeIdentifier(IdentifierTypeProto.EXCH_TICKER, 'AAPL')
|
|
107
|
+
);
|
|
108
|
+
security.addIdentifiers(
|
|
109
|
+
makeIdentifier(
|
|
110
|
+
IdentifierTypeProto.UNKNOWN_IDENTIFIER_TYPE,
|
|
111
|
+
'stale-uuid'
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
expect(() => validateIdentifiersForCreate(security)).toThrow(
|
|
115
|
+
IdentifierValidationError
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('skips link-mode securities (is_link=true)', () => {
|
|
120
|
+
// Link-mode = reference handle (uuid + as_of only); no identifiers
|
|
121
|
+
// attached. The guard must skip rather than over-trigger.
|
|
122
|
+
const link = new SecurityProto();
|
|
123
|
+
link.setIsLink(true);
|
|
124
|
+
expect(() => validateIdentifiersForCreate(link)).not.toThrow();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('passes on empty identifiers list (server enforces "at least one")', () => {
|
|
128
|
+
// Our consumer-side check polices the *type* of every attached
|
|
129
|
+
// identifier; the "must have ≥1 identifier" rule lives server-side.
|
|
130
|
+
const security = new SecurityProto();
|
|
131
|
+
expect(() => validateIdentifiersForCreate(security)).not.toThrow();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
import { IdentifierProto } from '../../../fintekkers/models/security/identifier/identifier_pb';
|
|
2
2
|
import { IdentifierTypeProto } from '../../../fintekkers/models/security/identifier/identifier_type_pb';
|
|
3
|
+
import { SecurityProto } from '../../../fintekkers/models/security/security_pb';
|
|
4
|
+
/**
|
|
5
|
+
* Raised when an IdentifierProto is rejected by the client-side guard
|
|
6
|
+
* before being sent to SecurityService. See FinTekkers/second-brain#347.
|
|
7
|
+
*/
|
|
8
|
+
export declare class IdentifierValidationError extends Error {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Client-side guard for a single IdentifierProto. Mirrors the server's
|
|
13
|
+
* SecurityAPIGRPCImpl.validateCreateRequest reject so consumer SDKs fail
|
|
14
|
+
* fast before the gRPC round-trip. See FinTekkers/second-brain#347.
|
|
15
|
+
*
|
|
16
|
+
* Rejects:
|
|
17
|
+
* - identifier_type == UNKNOWN_IDENTIFIER_TYPE (proto3 default — never a
|
|
18
|
+
* real identifier; equity loaders MUST pass EXCH_TICKER / CUSIP / ...)
|
|
19
|
+
* - identifier_value empty or whitespace-only
|
|
20
|
+
*/
|
|
21
|
+
export declare function validateIdentifierProto(identifier: IdentifierProto): void;
|
|
22
|
+
/**
|
|
23
|
+
* Client-side guard for every identifier carried by a SecurityProto on the
|
|
24
|
+
* create/upsert path. Skips link-mode securities (is_link=true) — those carry
|
|
25
|
+
* only uuid+as_of and aren't entities being created. Throws on the first
|
|
26
|
+
* offending identifier.
|
|
27
|
+
*/
|
|
28
|
+
export declare function validateIdentifiersForCreate(security: SecurityProto): void;
|
|
3
29
|
export declare class Identifier {
|
|
4
30
|
proto: IdentifierProto;
|
|
5
31
|
constructor(proto: IdentifierProto);
|
|
@@ -1,8 +1,58 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Identifier = void 0;
|
|
3
|
+
exports.Identifier = exports.validateIdentifiersForCreate = exports.validateIdentifierProto = exports.IdentifierValidationError = void 0;
|
|
4
4
|
const identifier_pb_1 = require("../../../fintekkers/models/security/identifier/identifier_pb");
|
|
5
5
|
const identifier_type_pb_1 = require("../../../fintekkers/models/security/identifier/identifier_type_pb");
|
|
6
|
+
/**
|
|
7
|
+
* Raised when an IdentifierProto is rejected by the client-side guard
|
|
8
|
+
* before being sent to SecurityService. See FinTekkers/second-brain#347.
|
|
9
|
+
*/
|
|
10
|
+
class IdentifierValidationError extends Error {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'IdentifierValidationError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.IdentifierValidationError = IdentifierValidationError;
|
|
17
|
+
/**
|
|
18
|
+
* Client-side guard for a single IdentifierProto. Mirrors the server's
|
|
19
|
+
* SecurityAPIGRPCImpl.validateCreateRequest reject so consumer SDKs fail
|
|
20
|
+
* fast before the gRPC round-trip. See FinTekkers/second-brain#347.
|
|
21
|
+
*
|
|
22
|
+
* Rejects:
|
|
23
|
+
* - identifier_type == UNKNOWN_IDENTIFIER_TYPE (proto3 default — never a
|
|
24
|
+
* real identifier; equity loaders MUST pass EXCH_TICKER / CUSIP / ...)
|
|
25
|
+
* - identifier_value empty or whitespace-only
|
|
26
|
+
*/
|
|
27
|
+
function validateIdentifierProto(identifier) {
|
|
28
|
+
var _a;
|
|
29
|
+
const idType = identifier.getIdentifierType();
|
|
30
|
+
if (idType === identifier_type_pb_1.IdentifierTypeProto.UNKNOWN_IDENTIFIER_TYPE) {
|
|
31
|
+
throw new IdentifierValidationError(`Refusing to send Security with identifier_type=UNKNOWN_IDENTIFIER_TYPE. ` +
|
|
32
|
+
`Pass a concrete IdentifierTypeProto (${Identifier.getAllTypeNames().join(', ')}). ` +
|
|
33
|
+
`See FinTekkers/second-brain#347.`);
|
|
34
|
+
}
|
|
35
|
+
const value = identifier.getIdentifierValue();
|
|
36
|
+
if (value === undefined || value === null || value.trim() === '') {
|
|
37
|
+
const typeName = (_a = Identifier.identifierTypeEnumMap.get(idType)) !== null && _a !== void 0 ? _a : String(idType);
|
|
38
|
+
throw new IdentifierValidationError(`Refusing to send Security identifier with empty identifier_value ` +
|
|
39
|
+
`(identifier_type=${typeName}). See FinTekkers/second-brain#347.`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.validateIdentifierProto = validateIdentifierProto;
|
|
43
|
+
/**
|
|
44
|
+
* Client-side guard for every identifier carried by a SecurityProto on the
|
|
45
|
+
* create/upsert path. Skips link-mode securities (is_link=true) — those carry
|
|
46
|
+
* only uuid+as_of and aren't entities being created. Throws on the first
|
|
47
|
+
* offending identifier.
|
|
48
|
+
*/
|
|
49
|
+
function validateIdentifiersForCreate(security) {
|
|
50
|
+
if (security.getIsLink()) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
security.getIdentifiersList().forEach(validateIdentifierProto);
|
|
54
|
+
}
|
|
55
|
+
exports.validateIdentifiersForCreate = validateIdentifiersForCreate;
|
|
6
56
|
class Identifier {
|
|
7
57
|
constructor(proto) {
|
|
8
58
|
this.proto = proto;
|
|
@@ -62,6 +112,9 @@ class Identifier {
|
|
|
62
112
|
const proto = new identifier_pb_1.IdentifierProto();
|
|
63
113
|
proto.setIdentifierType(enumValue);
|
|
64
114
|
proto.setIdentifierValue(value);
|
|
115
|
+
// Client-side guard (#347): rejects UNKNOWN_IDENTIFIER_TYPE and empty
|
|
116
|
+
// values here so misuse fails at construction time, not at send time.
|
|
117
|
+
validateIdentifierProto(proto);
|
|
65
118
|
return new Identifier(proto);
|
|
66
119
|
}
|
|
67
120
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identifier.js","sourceRoot":"","sources":["identifier.ts"],"names":[],"mappings":";;;AAAA,gGAA+F;AAC/F,0GAAwG;
|
|
1
|
+
{"version":3,"file":"identifier.js","sourceRoot":"","sources":["identifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -78,15 +78,12 @@ describe('Identifier.fromName', () => {
|
|
|
78
78
|
expect(err.message).toContain('CUSIP');
|
|
79
79
|
});
|
|
80
80
|
test('throws on UNKNOWN_IDENTIFIER_TYPE — sentinel is not a public name', () => {
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// flip this assertion.)
|
|
88
|
-
const id = identifier_1.Identifier.fromName('UNKNOWN_IDENTIFIER_TYPE', 'whatever');
|
|
89
|
-
expect(id.getIdentifierType()).toBe(identifier_type_pb_1.IdentifierTypeProto.UNKNOWN_IDENTIFIER_TYPE);
|
|
81
|
+
// The tightening anticipated by the earlier permissive test (now
|
|
82
|
+
// landed in FinTekkers/second-brain#347): the sentinel is rejected
|
|
83
|
+
// at construction time so consumer code cannot accidentally build
|
|
84
|
+
// and send a Security with the proto3-default identifier type.
|
|
85
|
+
expect(() => identifier_1.Identifier.fromName('UNKNOWN_IDENTIFIER_TYPE', 'whatever'))
|
|
86
|
+
.toThrow(/UNKNOWN_IDENTIFIER_TYPE/);
|
|
90
87
|
});
|
|
91
88
|
});
|
|
92
89
|
describe('Identifier.getAllTypeNames', () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identifier.test.js","sourceRoot":"","sources":["identifier.test.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"identifier.test.js","sourceRoot":"","sources":["identifier.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -94,15 +94,12 @@ describe('Identifier.fromName', () => {
|
|
|
94
94
|
});
|
|
95
95
|
|
|
96
96
|
test('throws on UNKNOWN_IDENTIFIER_TYPE — sentinel is not a public name', () => {
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
// flip this assertion.)
|
|
104
|
-
const id = Identifier.fromName('UNKNOWN_IDENTIFIER_TYPE', 'whatever');
|
|
105
|
-
expect(id.getIdentifierType()).toBe(IdentifierTypeProto.UNKNOWN_IDENTIFIER_TYPE);
|
|
97
|
+
// The tightening anticipated by the earlier permissive test (now
|
|
98
|
+
// landed in FinTekkers/second-brain#347): the sentinel is rejected
|
|
99
|
+
// at construction time so consumer code cannot accidentally build
|
|
100
|
+
// and send a Security with the proto3-default identifier type.
|
|
101
|
+
expect(() => Identifier.fromName('UNKNOWN_IDENTIFIER_TYPE', 'whatever'))
|
|
102
|
+
.toThrow(/UNKNOWN_IDENTIFIER_TYPE/);
|
|
106
103
|
});
|
|
107
104
|
});
|
|
108
105
|
|
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
import { IdentifierProto } from '../../../fintekkers/models/security/identifier/identifier_pb';
|
|
2
2
|
import { IdentifierTypeProto } from '../../../fintekkers/models/security/identifier/identifier_type_pb';
|
|
3
|
+
import { SecurityProto } from '../../../fintekkers/models/security/security_pb';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Raised when an IdentifierProto is rejected by the client-side guard
|
|
7
|
+
* before being sent to SecurityService. See FinTekkers/second-brain#347.
|
|
8
|
+
*/
|
|
9
|
+
export class IdentifierValidationError extends Error {
|
|
10
|
+
constructor(message: string) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'IdentifierValidationError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Client-side guard for a single IdentifierProto. Mirrors the server's
|
|
18
|
+
* SecurityAPIGRPCImpl.validateCreateRequest reject so consumer SDKs fail
|
|
19
|
+
* fast before the gRPC round-trip. See FinTekkers/second-brain#347.
|
|
20
|
+
*
|
|
21
|
+
* Rejects:
|
|
22
|
+
* - identifier_type == UNKNOWN_IDENTIFIER_TYPE (proto3 default — never a
|
|
23
|
+
* real identifier; equity loaders MUST pass EXCH_TICKER / CUSIP / ...)
|
|
24
|
+
* - identifier_value empty or whitespace-only
|
|
25
|
+
*/
|
|
26
|
+
export function validateIdentifierProto(identifier: IdentifierProto): void {
|
|
27
|
+
const idType = identifier.getIdentifierType();
|
|
28
|
+
if (idType === IdentifierTypeProto.UNKNOWN_IDENTIFIER_TYPE) {
|
|
29
|
+
throw new IdentifierValidationError(
|
|
30
|
+
`Refusing to send Security with identifier_type=UNKNOWN_IDENTIFIER_TYPE. ` +
|
|
31
|
+
`Pass a concrete IdentifierTypeProto (${Identifier.getAllTypeNames().join(', ')}). ` +
|
|
32
|
+
`See FinTekkers/second-brain#347.`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const value = identifier.getIdentifierValue();
|
|
36
|
+
if (value === undefined || value === null || value.trim() === '') {
|
|
37
|
+
const typeName =
|
|
38
|
+
Identifier.identifierTypeEnumMap.get(idType) ?? String(idType);
|
|
39
|
+
throw new IdentifierValidationError(
|
|
40
|
+
`Refusing to send Security identifier with empty identifier_value ` +
|
|
41
|
+
`(identifier_type=${typeName}). See FinTekkers/second-brain#347.`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Client-side guard for every identifier carried by a SecurityProto on the
|
|
48
|
+
* create/upsert path. Skips link-mode securities (is_link=true) — those carry
|
|
49
|
+
* only uuid+as_of and aren't entities being created. Throws on the first
|
|
50
|
+
* offending identifier.
|
|
51
|
+
*/
|
|
52
|
+
export function validateIdentifiersForCreate(security: SecurityProto): void {
|
|
53
|
+
if (security.getIsLink()) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
security.getIdentifiersList().forEach(validateIdentifierProto);
|
|
57
|
+
}
|
|
3
58
|
|
|
4
59
|
export class Identifier {
|
|
5
60
|
proto: IdentifierProto;
|
|
@@ -78,6 +133,9 @@ export class Identifier {
|
|
|
78
133
|
const proto = new IdentifierProto();
|
|
79
134
|
proto.setIdentifierType(enumValue);
|
|
80
135
|
proto.setIdentifierValue(value);
|
|
136
|
+
// Client-side guard (#347): rejects UNKNOWN_IDENTIFIER_TYPE and empty
|
|
137
|
+
// values here so misuse fails at construction time, not at send time.
|
|
138
|
+
validateIdentifierProto(proto);
|
|
81
139
|
return new Identifier(proto);
|
|
82
140
|
}
|
|
83
141
|
|
|
@@ -4,6 +4,7 @@ import { ZonedDateTime } from "../utils/datetime";
|
|
|
4
4
|
import { UUID } from "../utils/uuid";
|
|
5
5
|
import { IdentifierTypeProto } from "../../../fintekkers/models/security/identifier/identifier_type_pb";
|
|
6
6
|
import { Identifier } from "./identifier";
|
|
7
|
+
import LinkResolver from "../../util/link-resolver";
|
|
7
8
|
declare class Security {
|
|
8
9
|
proto: SecurityProto;
|
|
9
10
|
constructor(proto: SecurityProto);
|
|
@@ -27,15 +28,32 @@ declare class Security {
|
|
|
27
28
|
static linkOfLatest(uuid: UUID): SecurityProto;
|
|
28
29
|
private static _uuidToProto;
|
|
29
30
|
private static _zonedDateTimeToProto;
|
|
31
|
+
/**
|
|
32
|
+
* Async hydration — fetches the full proto via the default
|
|
33
|
+
* `LinkResolver` (or one you pass in) and swaps it onto this wrapper.
|
|
34
|
+
* Returns `this` so it can be chained.
|
|
35
|
+
*
|
|
36
|
+
* const sec = await new Security(linkProto).hydrate();
|
|
37
|
+
* console.log(sec.getIssuerName());
|
|
38
|
+
*
|
|
39
|
+
* Mirrors the Java / Python / Rust auto-resolve story — except in TS
|
|
40
|
+
* the fetch is necessarily async (no sync-from-async bridge in
|
|
41
|
+
* idiomatic Node.js), so the user pays one extra `await`. The default
|
|
42
|
+
* resolver is the process-wide singleton from
|
|
43
|
+
* `LinkResolver.getDefault()`; override per call by passing your own.
|
|
44
|
+
*
|
|
45
|
+
* On a non-link wrapper, this is a no-op and returns immediately.
|
|
46
|
+
*/
|
|
47
|
+
hydrate(resolver?: LinkResolver): Promise<this>;
|
|
30
48
|
/**
|
|
31
49
|
* Lazy hydration. If this Security is in link mode, swap in the resolved
|
|
32
50
|
* proto from LinkCache. On cache miss, throws — caller must pre-warm via
|
|
33
|
-
* LinkResolver. See docs/adr/lazy-link-hydration.md.
|
|
51
|
+
* LinkResolver or call `hydrate()` first. See docs/adr/lazy-link-hydration.md.
|
|
34
52
|
*
|
|
35
|
-
* TS variant is cache-only (no fetcher hook) because the gRPC stubs
|
|
36
|
-
* async and chaining the resolver into every getter would force every
|
|
37
|
-
* accessor to become async. Pre-warming through LinkResolver
|
|
38
|
-
* sync getter API.
|
|
53
|
+
* TS variant is cache-only (no sync fetcher hook) because the gRPC stubs
|
|
54
|
+
* are async and chaining the resolver into every getter would force every
|
|
55
|
+
* accessor to become async. Pre-warming through LinkResolver / `hydrate()`
|
|
56
|
+
* keeps the sync getter API.
|
|
39
57
|
*/
|
|
40
58
|
private ensureHydrated;
|
|
41
59
|
/**
|
|
@@ -22,6 +22,18 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
25
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
38
|
const field_pb_1 = require("../../../fintekkers/models/position/field_pb");
|
|
27
39
|
const security_pb_1 = require("../../../fintekkers/models/security/security_pb");
|
|
@@ -32,6 +44,7 @@ const product_type_pb_1 = require("../../../fintekkers/models/security/product_t
|
|
|
32
44
|
const identifier_1 = require("./identifier");
|
|
33
45
|
const product_hierarchy_1 = require("./product_hierarchy");
|
|
34
46
|
const LinkCacheModule = __importStar(require("../../util/link-cache"));
|
|
47
|
+
const link_resolver_1 = __importDefault(require("../../util/link-resolver"));
|
|
35
48
|
class Security {
|
|
36
49
|
constructor(proto) {
|
|
37
50
|
this.proto = proto;
|
|
@@ -77,15 +90,48 @@ class Security {
|
|
|
77
90
|
static _zonedDateTimeToProto(asOf) {
|
|
78
91
|
return asOf.toProto();
|
|
79
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Async hydration — fetches the full proto via the default
|
|
95
|
+
* `LinkResolver` (or one you pass in) and swaps it onto this wrapper.
|
|
96
|
+
* Returns `this` so it can be chained.
|
|
97
|
+
*
|
|
98
|
+
* const sec = await new Security(linkProto).hydrate();
|
|
99
|
+
* console.log(sec.getIssuerName());
|
|
100
|
+
*
|
|
101
|
+
* Mirrors the Java / Python / Rust auto-resolve story — except in TS
|
|
102
|
+
* the fetch is necessarily async (no sync-from-async bridge in
|
|
103
|
+
* idiomatic Node.js), so the user pays one extra `await`. The default
|
|
104
|
+
* resolver is the process-wide singleton from
|
|
105
|
+
* `LinkResolver.getDefault()`; override per call by passing your own.
|
|
106
|
+
*
|
|
107
|
+
* On a non-link wrapper, this is a no-op and returns immediately.
|
|
108
|
+
*/
|
|
109
|
+
hydrate(resolver) {
|
|
110
|
+
var _a;
|
|
111
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
112
|
+
if (!this.proto.getIsLink())
|
|
113
|
+
return this;
|
|
114
|
+
const uuidProto = this.proto.getUuid();
|
|
115
|
+
if (!uuidProto) {
|
|
116
|
+
throw new Error("Cannot hydrate a link-mode Security with no UUID set.");
|
|
117
|
+
}
|
|
118
|
+
const uuid = uuid_1.UUID.fromU8Array(uuidProto.getRawUuid_asU8());
|
|
119
|
+
const asOfProto = (_a = this.proto.getAsOf()) !== null && _a !== void 0 ? _a : undefined;
|
|
120
|
+
const r = resolver !== null && resolver !== void 0 ? resolver : link_resolver_1.default.getDefault();
|
|
121
|
+
const resolved = yield r.getSecurity(uuid, asOfProto);
|
|
122
|
+
this.proto = resolved.proto;
|
|
123
|
+
return this;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
80
126
|
/**
|
|
81
127
|
* Lazy hydration. If this Security is in link mode, swap in the resolved
|
|
82
128
|
* proto from LinkCache. On cache miss, throws — caller must pre-warm via
|
|
83
|
-
* LinkResolver. See docs/adr/lazy-link-hydration.md.
|
|
129
|
+
* LinkResolver or call `hydrate()` first. See docs/adr/lazy-link-hydration.md.
|
|
84
130
|
*
|
|
85
|
-
* TS variant is cache-only (no fetcher hook) because the gRPC stubs
|
|
86
|
-
* async and chaining the resolver into every getter would force every
|
|
87
|
-
* accessor to become async. Pre-warming through LinkResolver
|
|
88
|
-
* sync getter API.
|
|
131
|
+
* TS variant is cache-only (no sync fetcher hook) because the gRPC stubs
|
|
132
|
+
* are async and chaining the resolver into every getter would force every
|
|
133
|
+
* accessor to become async. Pre-warming through LinkResolver / `hydrate()`
|
|
134
|
+
* keeps the sync getter API.
|
|
89
135
|
*/
|
|
90
136
|
ensureHydrated() {
|
|
91
137
|
if (!this.proto.getIsLink())
|
|
@@ -103,7 +149,8 @@ class Security {
|
|
|
103
149
|
return;
|
|
104
150
|
}
|
|
105
151
|
throw new Error(`Cannot read fields on link-mode Security uuid=${uuidKey} `
|
|
106
|
-
+ `— LinkCache miss.
|
|
152
|
+
+ `— LinkCache miss. Call \`await security.hydrate()\` first, `
|
|
153
|
+
+ `or pre-warm via LinkResolver. `
|
|
107
154
|
+ `See docs/adr/lazy-link-hydration.md.`);
|
|
108
155
|
}
|
|
109
156
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.js","sourceRoot":"","sources":["security.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["security.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,6 +10,7 @@ import { IdentifierTypeProto } from "../../../fintekkers/models/security/identif
|
|
|
10
10
|
import { Identifier } from "./identifier";
|
|
11
11
|
import { isDescendantOf } from "./product_hierarchy";
|
|
12
12
|
import * as LinkCacheModule from "../../util/link-cache";
|
|
13
|
+
import LinkResolver from "../../util/link-resolver";
|
|
13
14
|
|
|
14
15
|
class Security {
|
|
15
16
|
proto: SecurityProto;
|
|
@@ -60,15 +61,45 @@ class Security {
|
|
|
60
61
|
return asOf.toProto();
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Async hydration — fetches the full proto via the default
|
|
66
|
+
* `LinkResolver` (or one you pass in) and swaps it onto this wrapper.
|
|
67
|
+
* Returns `this` so it can be chained.
|
|
68
|
+
*
|
|
69
|
+
* const sec = await new Security(linkProto).hydrate();
|
|
70
|
+
* console.log(sec.getIssuerName());
|
|
71
|
+
*
|
|
72
|
+
* Mirrors the Java / Python / Rust auto-resolve story — except in TS
|
|
73
|
+
* the fetch is necessarily async (no sync-from-async bridge in
|
|
74
|
+
* idiomatic Node.js), so the user pays one extra `await`. The default
|
|
75
|
+
* resolver is the process-wide singleton from
|
|
76
|
+
* `LinkResolver.getDefault()`; override per call by passing your own.
|
|
77
|
+
*
|
|
78
|
+
* On a non-link wrapper, this is a no-op and returns immediately.
|
|
79
|
+
*/
|
|
80
|
+
async hydrate(resolver?: LinkResolver): Promise<this> {
|
|
81
|
+
if (!this.proto.getIsLink()) return this;
|
|
82
|
+
const uuidProto = this.proto.getUuid();
|
|
83
|
+
if (!uuidProto) {
|
|
84
|
+
throw new Error("Cannot hydrate a link-mode Security with no UUID set.");
|
|
85
|
+
}
|
|
86
|
+
const uuid = UUID.fromU8Array(uuidProto.getRawUuid_asU8());
|
|
87
|
+
const asOfProto = this.proto.getAsOf() ?? undefined;
|
|
88
|
+
const r = resolver ?? LinkResolver.getDefault();
|
|
89
|
+
const resolved = await r.getSecurity(uuid, asOfProto);
|
|
90
|
+
this.proto = resolved.proto;
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
|
|
63
94
|
/**
|
|
64
95
|
* Lazy hydration. If this Security is in link mode, swap in the resolved
|
|
65
96
|
* proto from LinkCache. On cache miss, throws — caller must pre-warm via
|
|
66
|
-
* LinkResolver. See docs/adr/lazy-link-hydration.md.
|
|
97
|
+
* LinkResolver or call `hydrate()` first. See docs/adr/lazy-link-hydration.md.
|
|
67
98
|
*
|
|
68
|
-
* TS variant is cache-only (no fetcher hook) because the gRPC stubs
|
|
69
|
-
* async and chaining the resolver into every getter would force every
|
|
70
|
-
* accessor to become async. Pre-warming through LinkResolver
|
|
71
|
-
* sync getter API.
|
|
99
|
+
* TS variant is cache-only (no sync fetcher hook) because the gRPC stubs
|
|
100
|
+
* are async and chaining the resolver into every getter would force every
|
|
101
|
+
* accessor to become async. Pre-warming through LinkResolver / `hydrate()`
|
|
102
|
+
* keeps the sync getter API.
|
|
72
103
|
*/
|
|
73
104
|
private ensureHydrated(): void {
|
|
74
105
|
if (!this.proto.getIsLink()) return;
|
|
@@ -86,7 +117,8 @@ class Security {
|
|
|
86
117
|
}
|
|
87
118
|
throw new Error(
|
|
88
119
|
`Cannot read fields on link-mode Security uuid=${uuidKey} `
|
|
89
|
-
+ `— LinkCache miss.
|
|
120
|
+
+ `— LinkCache miss. Call \`await security.hydrate()\` first, `
|
|
121
|
+
+ `or pre-warm via LinkResolver. `
|
|
90
122
|
+ `See docs/adr/lazy-link-hydration.md.`
|
|
91
123
|
);
|
|
92
124
|
}
|