@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
|
@@ -9,6 +9,7 @@ import Security from '../../models/security/security';
|
|
|
9
9
|
// Model Utils
|
|
10
10
|
import { PositionFilter } from '../../models/position/positionfilter';
|
|
11
11
|
import * as dt from '../../models/utils/datetime';
|
|
12
|
+
import { validateIdentifiersForCreate } from '../../models/security/identifier';
|
|
12
13
|
|
|
13
14
|
// Requests & Services
|
|
14
15
|
import { SecurityClient } from '../../../fintekkers/services/security-service/security_service_grpc_pb';
|
|
@@ -18,6 +19,9 @@ import { CreateSecurityRequestProto } from '../../../fintekkers/requests/securit
|
|
|
18
19
|
import { CreateSecurityResponseProto } from '../../../fintekkers/requests/security/create_security_response_pb';
|
|
19
20
|
|
|
20
21
|
import EnvConfig from '../../models/utils/requestcontext';
|
|
22
|
+
import { UUID } from '../../models/utils/uuid';
|
|
23
|
+
import { ZonedDateTime } from '../../models/utils/datetime';
|
|
24
|
+
import * as LinkCacheModule from '../../util/link-cache';
|
|
21
25
|
|
|
22
26
|
class SecurityService {
|
|
23
27
|
private client: SecurityClient;
|
|
@@ -32,6 +36,12 @@ class SecurityService {
|
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
async validateCreateSecurity(security: SecurityProto): Promise<SummaryProto> {
|
|
39
|
+
// Client-side guard (#347): reject UNKNOWN_IDENTIFIER_TYPE and empty
|
|
40
|
+
// identifier values before the gRPC round-trip. Mirrors the server's
|
|
41
|
+
// validateCreateRequest reject so the dry-run RPC can't mask a request
|
|
42
|
+
// that the real createOrUpdate would also fail on.
|
|
43
|
+
validateIdentifiersForCreate(security);
|
|
44
|
+
|
|
35
45
|
const createRequest = new CreateSecurityRequestProto();
|
|
36
46
|
createRequest.setObjectClass('SecurityRequest');
|
|
37
47
|
createRequest.setVersion('0.0.1');
|
|
@@ -43,14 +53,29 @@ class SecurityService {
|
|
|
43
53
|
}
|
|
44
54
|
|
|
45
55
|
async createSecurity(security: SecurityProto): Promise<CreateSecurityResponseProto> {
|
|
56
|
+
// Client-side guard (#347): see validateCreateSecurity above.
|
|
57
|
+
validateIdentifiersForCreate(security);
|
|
58
|
+
|
|
46
59
|
const createRequest = new CreateSecurityRequestProto();
|
|
47
60
|
createRequest.setObjectClass('SecurityRequest');
|
|
48
61
|
createRequest.setVersion('0.0.1');
|
|
49
62
|
createRequest.setSecurityInput(security);
|
|
50
63
|
|
|
51
64
|
const createSecurityAsync = promisify(this.client.createOrUpdate.bind(this.client));
|
|
52
|
-
const response = await createSecurityAsync(createRequest);
|
|
53
|
-
|
|
65
|
+
const response = await createSecurityAsync(createRequest) as CreateSecurityResponseProto;
|
|
66
|
+
// Write-through to the process-wide LinkCache. Lazy-hydrate wrappers
|
|
67
|
+
// read from this cache, so a fresh persist becomes visible to subsequent
|
|
68
|
+
// accessor reads with no second RPC. See docs/adr/lazy-link-hydration.md.
|
|
69
|
+
const persisted = response.getSecurityResponse();
|
|
70
|
+
if (persisted) {
|
|
71
|
+
const uuidProto = persisted.getUuid();
|
|
72
|
+
const asOfProto = persisted.getAsOf();
|
|
73
|
+
if (uuidProto && asOfProto) {
|
|
74
|
+
const uuidKey = UUID.fromU8Array(uuidProto.getRawUuid_asU8()).toString();
|
|
75
|
+
LinkCacheModule.SECURITY.put(uuidKey, persisted, new ZonedDateTime(asOfProto));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return response;
|
|
54
79
|
}
|
|
55
80
|
|
|
56
81
|
async searchSecurityAsOfNow(positionFilter: PositionFilter): Promise<Security[]> {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// SecurityService client-side identifier guard (#347).
|
|
3
|
+
// Pins that createSecurity / validateCreateSecurity raise the guard BEFORE
|
|
4
|
+
// invoking the gRPC client when the request carries an UNKNOWN-typed or
|
|
5
|
+
// empty identifier. Pure unit test — never touches the wire; the client is
|
|
6
|
+
// replaced with a sentinel that fails the test if hit.
|
|
7
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
8
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
9
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
10
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
11
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
12
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
13
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
const identifier_pb_1 = require("../../fintekkers/models/security/identifier/identifier_pb");
|
|
18
|
+
const identifier_type_pb_1 = require("../../fintekkers/models/security/identifier/identifier_type_pb");
|
|
19
|
+
const security_pb_1 = require("../../fintekkers/models/security/security_pb");
|
|
20
|
+
const identifier_1 = require("../models/security/identifier");
|
|
21
|
+
const SecurityService_1 = require("./security-service/SecurityService");
|
|
22
|
+
function makeIdentifier(type, value) {
|
|
23
|
+
const p = new identifier_pb_1.IdentifierProto();
|
|
24
|
+
p.setIdentifierType(type);
|
|
25
|
+
p.setIdentifierValue(value);
|
|
26
|
+
return p;
|
|
27
|
+
}
|
|
28
|
+
function securityWith(identifier) {
|
|
29
|
+
const s = new security_pb_1.SecurityProto();
|
|
30
|
+
s.addIdentifiers(identifier);
|
|
31
|
+
return s;
|
|
32
|
+
}
|
|
33
|
+
function failingClient() {
|
|
34
|
+
// Any RPC hit means the guard didn't fire — the test should fail loud.
|
|
35
|
+
const explode = () => {
|
|
36
|
+
throw new Error('client-side guard must reject before invoking the stub');
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
createOrUpdate: explode,
|
|
40
|
+
validateCreateOrUpdate: explode,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
describe('SecurityService client-side identifier guard', () => {
|
|
44
|
+
test('createSecurity rejects UNKNOWN_IDENTIFIER_TYPE before RPC', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
45
|
+
const svc = new SecurityService_1.SecurityService();
|
|
46
|
+
svc.client = failingClient();
|
|
47
|
+
const bad = securityWith(makeIdentifier(identifier_type_pb_1.IdentifierTypeProto.UNKNOWN_IDENTIFIER_TYPE, 'stale-uuid'));
|
|
48
|
+
yield expect(svc.createSecurity(bad)).rejects.toBeInstanceOf(identifier_1.IdentifierValidationError);
|
|
49
|
+
}));
|
|
50
|
+
test('validateCreateSecurity rejects UNKNOWN_IDENTIFIER_TYPE before RPC', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
51
|
+
const svc = new SecurityService_1.SecurityService();
|
|
52
|
+
svc.client = failingClient();
|
|
53
|
+
const bad = securityWith(makeIdentifier(identifier_type_pb_1.IdentifierTypeProto.UNKNOWN_IDENTIFIER_TYPE, 'stale-uuid'));
|
|
54
|
+
yield expect(svc.validateCreateSecurity(bad)).rejects.toBeInstanceOf(identifier_1.IdentifierValidationError);
|
|
55
|
+
}));
|
|
56
|
+
test('createSecurity rejects empty identifier_value before RPC', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
57
|
+
const svc = new SecurityService_1.SecurityService();
|
|
58
|
+
svc.client = failingClient();
|
|
59
|
+
const bad = securityWith(makeIdentifier(identifier_type_pb_1.IdentifierTypeProto.EXCH_TICKER, ''));
|
|
60
|
+
yield expect(svc.createSecurity(bad)).rejects.toBeInstanceOf(identifier_1.IdentifierValidationError);
|
|
61
|
+
}));
|
|
62
|
+
});
|
|
63
|
+
//# sourceMappingURL=security.identifier-guard.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.identifier-guard.test.js","sourceRoot":"","sources":["security.identifier-guard.test.ts"],"names":[],"mappings":";AAAA,uDAAuD;AACvD,2EAA2E;AAC3E,wEAAwE;AACxE,2EAA2E;AAC3E,uDAAuD;;;;;;;;;;;AAEvD,6FAA4F;AAC5F,uGAAqG;AACrG,8EAA6E;AAC7E,8DAA0E;AAC1E,wEAAqE;AAErE,SAAS,cAAc,CAAC,IAAyB,EAAE,KAAa;IAC5D,MAAM,CAAC,GAAG,IAAI,+BAAe,EAAE,CAAC;IAChC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC5B,OAAO,CAAC,CAAC;AACb,CAAC;AAED,SAAS,YAAY,CAAC,UAA2B;IAC7C,MAAM,CAAC,GAAG,IAAI,2BAAa,EAAE,CAAC;IAC9B,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IAC7B,OAAO,CAAC,CAAC;AACb,CAAC;AAED,SAAS,aAAa;IAClB,uEAAuE;IACvE,MAAM,OAAO,GAAG,GAAG,EAAE;QACjB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC9E,CAAC,CAAC;IACF,OAAO;QACH,cAAc,EAAE,OAAO;QACvB,sBAAsB,EAAE,OAAO;KAClC,CAAC;AACN,CAAC;AAED,QAAQ,CAAC,8CAA8C,EAAE,GAAG,EAAE;IAC1D,IAAI,CAAC,2DAA2D,EAAE,GAAS,EAAE;QACzE,MAAM,GAAG,GAAG,IAAI,iCAAe,EAAE,CAAC;QACjC,GAAW,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,YAAY,CACpB,cAAc,CAAC,wCAAmB,CAAC,uBAAuB,EAAE,YAAY,CAAC,CAC5E,CAAC;QACF,MAAM,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CACxD,sCAAyB,CAC5B,CAAC;IACN,CAAC,CAAA,CAAC,CAAC;IAEH,IAAI,CAAC,mEAAmE,EAAE,GAAS,EAAE;QACjF,MAAM,GAAG,GAAG,IAAI,iCAAe,EAAE,CAAC;QACjC,GAAW,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,YAAY,CACpB,cAAc,CAAC,wCAAmB,CAAC,uBAAuB,EAAE,YAAY,CAAC,CAC5E,CAAC;QACF,MAAM,MAAM,CAAC,GAAG,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAChE,sCAAyB,CAC5B,CAAC;IACN,CAAC,CAAA,CAAC,CAAC;IAEH,IAAI,CAAC,0DAA0D,EAAE,GAAS,EAAE;QACxE,MAAM,GAAG,GAAG,IAAI,iCAAe,EAAE,CAAC;QACjC,GAAW,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,YAAY,CACpB,cAAc,CAAC,wCAAmB,CAAC,WAAW,EAAE,EAAE,CAAC,CACtD,CAAC;QACF,MAAM,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CACxD,sCAAyB,CAC5B,CAAC;IACN,CAAC,CAAA,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// SecurityService client-side identifier guard (#347).
|
|
2
|
+
// Pins that createSecurity / validateCreateSecurity raise the guard BEFORE
|
|
3
|
+
// invoking the gRPC client when the request carries an UNKNOWN-typed or
|
|
4
|
+
// empty identifier. Pure unit test — never touches the wire; the client is
|
|
5
|
+
// replaced with a sentinel that fails the test if hit.
|
|
6
|
+
|
|
7
|
+
import { IdentifierProto } from '../../fintekkers/models/security/identifier/identifier_pb';
|
|
8
|
+
import { IdentifierTypeProto } from '../../fintekkers/models/security/identifier/identifier_type_pb';
|
|
9
|
+
import { SecurityProto } from '../../fintekkers/models/security/security_pb';
|
|
10
|
+
import { IdentifierValidationError } from '../models/security/identifier';
|
|
11
|
+
import { SecurityService } from './security-service/SecurityService';
|
|
12
|
+
|
|
13
|
+
function makeIdentifier(type: IdentifierTypeProto, value: string): IdentifierProto {
|
|
14
|
+
const p = new IdentifierProto();
|
|
15
|
+
p.setIdentifierType(type);
|
|
16
|
+
p.setIdentifierValue(value);
|
|
17
|
+
return p;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function securityWith(identifier: IdentifierProto): SecurityProto {
|
|
21
|
+
const s = new SecurityProto();
|
|
22
|
+
s.addIdentifiers(identifier);
|
|
23
|
+
return s;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function failingClient(): any {
|
|
27
|
+
// Any RPC hit means the guard didn't fire — the test should fail loud.
|
|
28
|
+
const explode = () => {
|
|
29
|
+
throw new Error('client-side guard must reject before invoking the stub');
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
createOrUpdate: explode,
|
|
33
|
+
validateCreateOrUpdate: explode,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe('SecurityService client-side identifier guard', () => {
|
|
38
|
+
test('createSecurity rejects UNKNOWN_IDENTIFIER_TYPE before RPC', async () => {
|
|
39
|
+
const svc = new SecurityService();
|
|
40
|
+
(svc as any).client = failingClient();
|
|
41
|
+
const bad = securityWith(
|
|
42
|
+
makeIdentifier(IdentifierTypeProto.UNKNOWN_IDENTIFIER_TYPE, 'stale-uuid')
|
|
43
|
+
);
|
|
44
|
+
await expect(svc.createSecurity(bad)).rejects.toBeInstanceOf(
|
|
45
|
+
IdentifierValidationError
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('validateCreateSecurity rejects UNKNOWN_IDENTIFIER_TYPE before RPC', async () => {
|
|
50
|
+
const svc = new SecurityService();
|
|
51
|
+
(svc as any).client = failingClient();
|
|
52
|
+
const bad = securityWith(
|
|
53
|
+
makeIdentifier(IdentifierTypeProto.UNKNOWN_IDENTIFIER_TYPE, 'stale-uuid')
|
|
54
|
+
);
|
|
55
|
+
await expect(svc.validateCreateSecurity(bad)).rejects.toBeInstanceOf(
|
|
56
|
+
IdentifierValidationError
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('createSecurity rejects empty identifier_value before RPC', async () => {
|
|
61
|
+
const svc = new SecurityService();
|
|
62
|
+
(svc as any).client = failingClient();
|
|
63
|
+
const bad = securityWith(
|
|
64
|
+
makeIdentifier(IdentifierTypeProto.EXCH_TICKER, '')
|
|
65
|
+
);
|
|
66
|
+
await expect(svc.createSecurity(bad)).rejects.toBeInstanceOf(
|
|
67
|
+
IdentifierValidationError
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Verifies SecurityService / PortfolioService / PriceService / TransactionService
|
|
3
|
+
// each populate LinkCache on a successful createOrUpdate. Pure unit tests —
|
|
4
|
+
// the gRPC client is replaced via Object.assign on the service instance so we
|
|
5
|
+
// never hit the wire.
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
23
|
+
if (mod && mod.__esModule) return mod;
|
|
24
|
+
var result = {};
|
|
25
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
26
|
+
__setModuleDefault(result, mod);
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
29
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
30
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
31
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
32
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
33
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
34
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
35
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
|
+
};
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
const uuid_1 = require("../models/utils/uuid");
|
|
43
|
+
const datetime_1 = require("../models/utils/datetime");
|
|
44
|
+
const local_timestamp_pb_1 = require("../../fintekkers/models/util/local_timestamp_pb");
|
|
45
|
+
const timestamp_pb_1 = require("google-protobuf/google/protobuf/timestamp_pb");
|
|
46
|
+
const security_pb_1 = require("../../fintekkers/models/security/security_pb");
|
|
47
|
+
const portfolio_pb_1 = require("../../fintekkers/models/portfolio/portfolio_pb");
|
|
48
|
+
const price_pb_1 = require("../../fintekkers/models/price/price_pb");
|
|
49
|
+
const transaction_pb_1 = require("../../fintekkers/models/transaction/transaction_pb");
|
|
50
|
+
const create_security_response_pb_1 = require("../../fintekkers/requests/security/create_security_response_pb");
|
|
51
|
+
const create_portfolio_response_pb_1 = require("../../fintekkers/requests/portfolio/create_portfolio_response_pb");
|
|
52
|
+
const create_price_response_pb_1 = require("../../fintekkers/requests/price/create_price_response_pb");
|
|
53
|
+
const create_transaction_response_pb_1 = require("../../fintekkers/requests/transaction/create_transaction_response_pb");
|
|
54
|
+
const SecurityService_1 = require("./security-service/SecurityService");
|
|
55
|
+
const PortfolioService_1 = require("./portfolio-service/PortfolioService");
|
|
56
|
+
const PriceService_1 = require("./price-service/PriceService");
|
|
57
|
+
const TransactionService_1 = require("./transaction-service/TransactionService");
|
|
58
|
+
const transaction_1 = __importDefault(require("../models/transaction/transaction"));
|
|
59
|
+
const LinkCacheModule = __importStar(require("../util/link-cache"));
|
|
60
|
+
function makeAsOf(epochSecondsOffset = 0) {
|
|
61
|
+
const ts = new timestamp_pb_1.Timestamp();
|
|
62
|
+
ts.setSeconds(1700000000 + epochSecondsOffset);
|
|
63
|
+
ts.setNanos(0);
|
|
64
|
+
const proto = new local_timestamp_pb_1.LocalTimestampProto();
|
|
65
|
+
proto.setTimestamp(ts);
|
|
66
|
+
proto.setTimeZone('UTC');
|
|
67
|
+
return proto;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Build a fake gRPC client that returns a pre-canned response on
|
|
71
|
+
* createOrUpdate. Mimics the callback shape the real grpc-js client uses,
|
|
72
|
+
* since the service wraps it with `promisify`.
|
|
73
|
+
*/
|
|
74
|
+
function fakeClient(response) {
|
|
75
|
+
return {
|
|
76
|
+
createOrUpdate(_req, cb) {
|
|
77
|
+
cb(null, response);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
LinkCacheModule.SECURITY.clear();
|
|
83
|
+
LinkCacheModule.PORTFOLIO.clear();
|
|
84
|
+
LinkCacheModule.PRICE.clear();
|
|
85
|
+
LinkCacheModule.TRANSACTION.clear();
|
|
86
|
+
});
|
|
87
|
+
test('SecurityService.createSecurity populates LinkCache.SECURITY', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
88
|
+
const uuid = uuid_1.UUID.random();
|
|
89
|
+
const asOf = makeAsOf(0);
|
|
90
|
+
const persisted = new security_pb_1.SecurityProto();
|
|
91
|
+
persisted.setUuid(uuid.toUUIDProto());
|
|
92
|
+
persisted.setAsOf(asOf);
|
|
93
|
+
persisted.setIssuerName('ACME');
|
|
94
|
+
const response = new create_security_response_pb_1.CreateSecurityResponseProto();
|
|
95
|
+
response.setSecurityResponse(persisted);
|
|
96
|
+
const svc = new SecurityService_1.SecurityService();
|
|
97
|
+
svc.client = fakeClient(response);
|
|
98
|
+
yield svc.createSecurity(new security_pb_1.SecurityProto());
|
|
99
|
+
const cached = LinkCacheModule.SECURITY.get(uuid.toString(), new datetime_1.ZonedDateTime(asOf));
|
|
100
|
+
expect(cached).toBeDefined();
|
|
101
|
+
expect(cached.getIssuerName()).toBe('ACME');
|
|
102
|
+
}));
|
|
103
|
+
test('PortfolioService.createPortfolio populates LinkCache.PORTFOLIO', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
104
|
+
const uuid = uuid_1.UUID.random();
|
|
105
|
+
const asOf = makeAsOf(1);
|
|
106
|
+
const persisted = new portfolio_pb_1.PortfolioProto();
|
|
107
|
+
persisted.setUuid(uuid.toUUIDProto());
|
|
108
|
+
persisted.setAsOf(asOf);
|
|
109
|
+
persisted.setPortfolioName('Strategy Z');
|
|
110
|
+
const response = new create_portfolio_response_pb_1.CreatePortfolioResponseProto();
|
|
111
|
+
response.addPortfolioResponse(persisted);
|
|
112
|
+
const svc = new PortfolioService_1.PortfolioService();
|
|
113
|
+
svc.client = fakeClient(response);
|
|
114
|
+
yield svc.createPortfolio(new portfolio_pb_1.PortfolioProto());
|
|
115
|
+
const cached = LinkCacheModule.PORTFOLIO.get(uuid.toString(), new datetime_1.ZonedDateTime(asOf));
|
|
116
|
+
expect(cached).toBeDefined();
|
|
117
|
+
expect(cached.getPortfolioName()).toBe('Strategy Z');
|
|
118
|
+
}));
|
|
119
|
+
test('PriceService.createOrUpdate populates LinkCache.PRICE', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
120
|
+
const uuid = uuid_1.UUID.random();
|
|
121
|
+
const asOf = makeAsOf(2);
|
|
122
|
+
const persisted = new price_pb_1.PriceProto();
|
|
123
|
+
persisted.setUuid(uuid.toUUIDProto());
|
|
124
|
+
persisted.setAsOf(asOf);
|
|
125
|
+
const response = new create_price_response_pb_1.CreatePriceResponseProto();
|
|
126
|
+
response.addPriceResponse(persisted);
|
|
127
|
+
const svc = new PriceService_1.PriceService();
|
|
128
|
+
svc.client = fakeClient(response);
|
|
129
|
+
yield svc.createOrUpdate(new price_pb_1.PriceProto());
|
|
130
|
+
const cached = LinkCacheModule.PRICE.get(uuid.toString(), new datetime_1.ZonedDateTime(asOf));
|
|
131
|
+
expect(cached).toBeDefined();
|
|
132
|
+
}));
|
|
133
|
+
test('TransactionService.createTransaction populates LinkCache.TRANSACTION', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
134
|
+
const uuid = uuid_1.UUID.random();
|
|
135
|
+
const asOf = makeAsOf(3);
|
|
136
|
+
const persisted = new transaction_pb_1.TransactionProto();
|
|
137
|
+
persisted.setUuid(uuid.toUUIDProto());
|
|
138
|
+
persisted.setAsOf(asOf);
|
|
139
|
+
const response = new create_transaction_response_pb_1.CreateTransactionResponseProto();
|
|
140
|
+
response.setTransactionResponse(persisted);
|
|
141
|
+
const svc = new TransactionService_1.TransactionService();
|
|
142
|
+
svc.client = fakeClient(response);
|
|
143
|
+
yield svc.createTransaction(new transaction_1.default(new transaction_pb_1.TransactionProto()));
|
|
144
|
+
const cached = LinkCacheModule.TRANSACTION.get(uuid.toString(), new datetime_1.ZonedDateTime(asOf));
|
|
145
|
+
expect(cached).toBeDefined();
|
|
146
|
+
}));
|
|
147
|
+
//# sourceMappingURL=service-client-writethrough.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-client-writethrough.test.js","sourceRoot":"","sources":["service-client-writethrough.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// Verifies SecurityService / PortfolioService / PriceService / TransactionService
|
|
2
|
+
// each populate LinkCache on a successful createOrUpdate. Pure unit tests —
|
|
3
|
+
// the gRPC client is replaced via Object.assign on the service instance so we
|
|
4
|
+
// never hit the wire.
|
|
5
|
+
|
|
6
|
+
import { UUID } from '../models/utils/uuid';
|
|
7
|
+
import { ZonedDateTime } from '../models/utils/datetime';
|
|
8
|
+
import { LocalTimestampProto } from '../../fintekkers/models/util/local_timestamp_pb';
|
|
9
|
+
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
|
10
|
+
|
|
11
|
+
import { SecurityProto } from '../../fintekkers/models/security/security_pb';
|
|
12
|
+
import { PortfolioProto } from '../../fintekkers/models/portfolio/portfolio_pb';
|
|
13
|
+
import { PriceProto } from '../../fintekkers/models/price/price_pb';
|
|
14
|
+
import { TransactionProto } from '../../fintekkers/models/transaction/transaction_pb';
|
|
15
|
+
|
|
16
|
+
import { CreateSecurityResponseProto } from '../../fintekkers/requests/security/create_security_response_pb';
|
|
17
|
+
import { CreatePortfolioResponseProto } from '../../fintekkers/requests/portfolio/create_portfolio_response_pb';
|
|
18
|
+
import { CreatePriceResponseProto } from '../../fintekkers/requests/price/create_price_response_pb';
|
|
19
|
+
import { CreateTransactionResponseProto } from '../../fintekkers/requests/transaction/create_transaction_response_pb';
|
|
20
|
+
|
|
21
|
+
import { SecurityService } from './security-service/SecurityService';
|
|
22
|
+
import { PortfolioService } from './portfolio-service/PortfolioService';
|
|
23
|
+
import { PriceService } from './price-service/PriceService';
|
|
24
|
+
import { TransactionService } from './transaction-service/TransactionService';
|
|
25
|
+
import Transaction from '../models/transaction/transaction';
|
|
26
|
+
|
|
27
|
+
import * as LinkCacheModule from '../util/link-cache';
|
|
28
|
+
|
|
29
|
+
function makeAsOf(epochSecondsOffset = 0): LocalTimestampProto {
|
|
30
|
+
const ts = new Timestamp();
|
|
31
|
+
ts.setSeconds(1_700_000_000 + epochSecondsOffset);
|
|
32
|
+
ts.setNanos(0);
|
|
33
|
+
const proto = new LocalTimestampProto();
|
|
34
|
+
proto.setTimestamp(ts);
|
|
35
|
+
proto.setTimeZone('UTC');
|
|
36
|
+
return proto;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build a fake gRPC client that returns a pre-canned response on
|
|
41
|
+
* createOrUpdate. Mimics the callback shape the real grpc-js client uses,
|
|
42
|
+
* since the service wraps it with `promisify`.
|
|
43
|
+
*/
|
|
44
|
+
function fakeClient<TRes>(response: TRes): any {
|
|
45
|
+
return {
|
|
46
|
+
createOrUpdate(_req: unknown, cb: (err: unknown, res: TRes) => void) {
|
|
47
|
+
cb(null, response);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
LinkCacheModule.SECURITY.clear();
|
|
54
|
+
LinkCacheModule.PORTFOLIO.clear();
|
|
55
|
+
LinkCacheModule.PRICE.clear();
|
|
56
|
+
LinkCacheModule.TRANSACTION.clear();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('SecurityService.createSecurity populates LinkCache.SECURITY', async () => {
|
|
60
|
+
const uuid = UUID.random();
|
|
61
|
+
const asOf = makeAsOf(0);
|
|
62
|
+
|
|
63
|
+
const persisted = new SecurityProto();
|
|
64
|
+
persisted.setUuid(uuid.toUUIDProto());
|
|
65
|
+
persisted.setAsOf(asOf);
|
|
66
|
+
persisted.setIssuerName('ACME');
|
|
67
|
+
|
|
68
|
+
const response = new CreateSecurityResponseProto();
|
|
69
|
+
response.setSecurityResponse(persisted);
|
|
70
|
+
|
|
71
|
+
const svc = new SecurityService();
|
|
72
|
+
(svc as any).client = fakeClient(response);
|
|
73
|
+
|
|
74
|
+
await svc.createSecurity(new SecurityProto());
|
|
75
|
+
|
|
76
|
+
const cached = LinkCacheModule.SECURITY.get(uuid.toString(), new ZonedDateTime(asOf));
|
|
77
|
+
expect(cached).toBeDefined();
|
|
78
|
+
expect(cached!.getIssuerName()).toBe('ACME');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('PortfolioService.createPortfolio populates LinkCache.PORTFOLIO', async () => {
|
|
82
|
+
const uuid = UUID.random();
|
|
83
|
+
const asOf = makeAsOf(1);
|
|
84
|
+
|
|
85
|
+
const persisted = new PortfolioProto();
|
|
86
|
+
persisted.setUuid(uuid.toUUIDProto());
|
|
87
|
+
persisted.setAsOf(asOf);
|
|
88
|
+
persisted.setPortfolioName('Strategy Z');
|
|
89
|
+
|
|
90
|
+
const response = new CreatePortfolioResponseProto();
|
|
91
|
+
response.addPortfolioResponse(persisted);
|
|
92
|
+
|
|
93
|
+
const svc = new PortfolioService();
|
|
94
|
+
(svc as any).client = fakeClient(response);
|
|
95
|
+
|
|
96
|
+
await svc.createPortfolio(new PortfolioProto());
|
|
97
|
+
|
|
98
|
+
const cached = LinkCacheModule.PORTFOLIO.get(uuid.toString(), new ZonedDateTime(asOf));
|
|
99
|
+
expect(cached).toBeDefined();
|
|
100
|
+
expect(cached!.getPortfolioName()).toBe('Strategy Z');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('PriceService.createOrUpdate populates LinkCache.PRICE', async () => {
|
|
104
|
+
const uuid = UUID.random();
|
|
105
|
+
const asOf = makeAsOf(2);
|
|
106
|
+
|
|
107
|
+
const persisted = new PriceProto();
|
|
108
|
+
persisted.setUuid(uuid.toUUIDProto());
|
|
109
|
+
persisted.setAsOf(asOf);
|
|
110
|
+
|
|
111
|
+
const response = new CreatePriceResponseProto();
|
|
112
|
+
response.addPriceResponse(persisted);
|
|
113
|
+
|
|
114
|
+
const svc = new PriceService();
|
|
115
|
+
(svc as any).client = fakeClient(response);
|
|
116
|
+
|
|
117
|
+
await svc.createOrUpdate(new PriceProto());
|
|
118
|
+
|
|
119
|
+
const cached = LinkCacheModule.PRICE.get(uuid.toString(), new ZonedDateTime(asOf));
|
|
120
|
+
expect(cached).toBeDefined();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('TransactionService.createTransaction populates LinkCache.TRANSACTION', async () => {
|
|
124
|
+
const uuid = UUID.random();
|
|
125
|
+
const asOf = makeAsOf(3);
|
|
126
|
+
|
|
127
|
+
const persisted = new TransactionProto();
|
|
128
|
+
persisted.setUuid(uuid.toUUIDProto());
|
|
129
|
+
persisted.setAsOf(asOf);
|
|
130
|
+
|
|
131
|
+
const response = new CreateTransactionResponseProto();
|
|
132
|
+
response.setTransactionResponse(persisted);
|
|
133
|
+
|
|
134
|
+
const svc = new TransactionService();
|
|
135
|
+
(svc as any).client = fakeClient(response);
|
|
136
|
+
|
|
137
|
+
await svc.createTransaction(new Transaction(new TransactionProto()));
|
|
138
|
+
|
|
139
|
+
const cached = LinkCacheModule.TRANSACTION.get(uuid.toString(), new ZonedDateTime(asOf));
|
|
140
|
+
expect(cached).toBeDefined();
|
|
141
|
+
});
|
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
26
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
27
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -23,6 +46,9 @@ const create_transaction_request_pb_1 = require("../../../fintekkers/requests/tr
|
|
|
23
46
|
const query_transaction_request_pb_1 = require("../../../fintekkers/requests/transaction/query_transaction_request_pb");
|
|
24
47
|
const requestcontext_1 = __importDefault(require("../../models/utils/requestcontext"));
|
|
25
48
|
const link_resolver_1 = __importDefault(require("../../util/link-resolver"));
|
|
49
|
+
const LinkCacheModule = __importStar(require("../../util/link-cache"));
|
|
50
|
+
const uuid_1 = require("../../models/utils/uuid");
|
|
51
|
+
const datetime_1 = require("../../models/utils/datetime");
|
|
26
52
|
class TransactionService {
|
|
27
53
|
constructor(apiKey) {
|
|
28
54
|
if (apiKey) {
|
|
@@ -52,6 +78,16 @@ class TransactionService {
|
|
|
52
78
|
createRequest.setCreateTransactionInput(transaction.proto);
|
|
53
79
|
const createTransactionAsync = (0, util_1.promisify)(this.client.createOrUpdate.bind(this.client));
|
|
54
80
|
const response = yield createTransactionAsync(createRequest);
|
|
81
|
+
// Write-through to LinkCache. transactionResponse is a singular field.
|
|
82
|
+
const persisted = response.getTransactionResponse();
|
|
83
|
+
if (persisted) {
|
|
84
|
+
const uuidProto = persisted.getUuid();
|
|
85
|
+
const asOfProto = persisted.getAsOf();
|
|
86
|
+
if (uuidProto && asOfProto) {
|
|
87
|
+
const uuidKey = uuid_1.UUID.fromU8Array(uuidProto.getRawUuid_asU8()).toString();
|
|
88
|
+
LinkCacheModule.TRANSACTION.put(uuidKey, persisted, new datetime_1.ZonedDateTime(asOfProto));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
55
91
|
return response;
|
|
56
92
|
});
|
|
57
93
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransactionService.js","sourceRoot":"","sources":["TransactionService.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TransactionService.js","sourceRoot":"","sources":["TransactionService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+BAAiC;AAEjC,SAAS;AACT,uFAA+D;AAK/D,cAAc;AAEd,sBAAsB;AACtB,8HAAiH;AACjH,0HAAuH;AAEvH,wHAAqH;AAErH,uFAA0D;AAC1D,6EAAoD;AACpD,uEAAyD;AACzD,kDAA+C;AAC/C,0DAA4D;AAG5D,MAAM,kBAAkB;IAGtB,YAAY,MAAe;QACzB,IAAI,MAAM,EAAE;YACV,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,wBAAS,CAAC,6BAA6B,CAAC,MAAM,CAAC,CAAC;YACtF,IAAI,CAAC,MAAM,GAAG,IAAI,+CAAiB,CAAC,wBAAS,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;SACtF;aAAM;YACL,IAAI,CAAC,MAAM,GAAG,IAAI,+CAAiB,CAAC,wBAAS,CAAC,MAAM,EAAE,wBAAS,CAAC,cAAc,CAAC,CAAC;SACjF;IACH,CAAC;IAEK,yBAAyB,CAAC,WAAwB;;YACtD,MAAM,aAAa,GAAG,IAAI,6DAA6B,EAAE,CAAC;YAC1D,aAAa,CAAC,cAAc,CAAC,oBAAoB,CAAC,CAAC;YACnD,aAAa,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAClC,aAAa,CAAC,yBAAyB,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YAE3D,MAAM,2BAA2B,GAAG,IAAA,gBAAS,EAAC,IAAI,CAAC,MAAM,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YACpG,MAAM,QAAQ,GAAG,MAAM,2BAA2B,CAAC,aAAa,CAAiB,CAAC;YAClF,OAAO,QAAQ,CAAC;QAClB,CAAC;KAAA;IAEK,iBAAiB,CAAC,WAAwB;;YAC9C,MAAM,aAAa,GAAG,IAAI,6DAA6B,EAAE,CAAC;YAC1D,aAAa,CAAC,cAAc,CAAC,oBAAoB,CAAC,CAAC;YACnD,aAAa,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAClC,aAAa,CAAC,yBAAyB,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YAE3D,MAAM,sBAAsB,GAAG,IAAA,gBAAS,EAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YACvF,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAAC,aAAa,CAAmC,CAAC;YAC/F,uEAAuE;YACvE,MAAM,SAAS,GAAG,QAAQ,CAAC,sBAAsB,EAAE,CAAC;YACpD,IAAI,SAAS,EAAE;gBACb,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;gBACtC,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;gBACtC,IAAI,SAAS,IAAI,SAAS,EAAE;oBAC1B,MAAM,OAAO,GAAG,WAAI,CAAC,WAAW,CAAC,SAAS,CAAC,eAAe,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;oBACzE,eAAe,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,wBAAa,CAAC,SAAS,CAAC,CAAC,CAAC;iBACnF;aACF;YACD,OAAO,QAAQ,CAAC;QAClB,CAAC;KAAA;IAED,iBAAiB,CAAC,IAAyB,EAAE,cAA8B,EAAE,aAAqB,GAAG;QAEnG,MAAM,aAAa,GAAG,IAAI,2DAA4B,EAAE,CAAC;QACzD,aAAa,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;QAChD,aAAa,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAClC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAE5B,aAAa,CAAC,yBAAyB,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC;QAClE,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAEnC,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC;QAE9B,SAAe,0BAA0B;;gBACvC,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;gBAChD,IAAI,OAAO,GAAkB,EAAE,CAAC;gBAEhC,OAAO,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;oBACpD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,QAAuC,EAAE,EAAE;wBAC7D,QAAQ,CAAC,0BAA0B,EAAE,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE;4BAC5D,MAAM,GAAG,GAAgB,IAAI,qBAAW,CAAC,WAAW,CAAC,CAAC;4BACtD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;wBACpB,CAAC,CAAC,CAAA;oBACJ,CAAC,CAAC,CAAC;oBAEH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;wBACrB,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;wBAClD,OAAO,CAAC,OAAO,CAAC,CAAC;oBACnB,CAAC,CAAC,CAAC;oBAEH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;wBAC1B,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC;wBAC3C,MAAM,CAAC,GAAG,CAAC,CAAC;oBACd,CAAC,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;YACL,CAAC;SAAA;QAED,OAAO,0BAA0B,EAAE,CAAC;IACtC,CAAC;IAED;;;;;;;;;;;OAWG;IACG,8BAA8B,CAClC,IAAyB,EACzB,cAA8B,EAC9B,aAAqB,GAAG,EACxB,YAA2B;;YAE3B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC;YAC5E,MAAM,QAAQ,GAAG,YAAY,aAAZ,YAAY,cAAZ,YAAY,GAAI,IAAI,uBAAY,EAAE,CAAC;YACpD,+DAA+D;YAC/D,MAAM,OAAO,CAAC,GAAG,CAAC;gBAChB,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC;gBAChC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC;aACjC,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;KAAA;CACF;AAEQ,gDAAkB"}
|
|
@@ -16,6 +16,9 @@ import { QueryTransactionRequestProto } from '../../../fintekkers/requests/trans
|
|
|
16
16
|
import { QueryTransactionResponseProto } from '../../../fintekkers/requests/transaction/query_transaction_response_pb';
|
|
17
17
|
import EnvConfig from '../../models/utils/requestcontext';
|
|
18
18
|
import LinkResolver from '../../util/link-resolver';
|
|
19
|
+
import * as LinkCacheModule from '../../util/link-cache';
|
|
20
|
+
import { UUID } from '../../models/utils/uuid';
|
|
21
|
+
import { ZonedDateTime } from '../../models/utils/datetime';
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
class TransactionService {
|
|
@@ -49,6 +52,16 @@ class TransactionService {
|
|
|
49
52
|
|
|
50
53
|
const createTransactionAsync = promisify(this.client.createOrUpdate.bind(this.client));
|
|
51
54
|
const response = await createTransactionAsync(createRequest) as CreateTransactionResponseProto;
|
|
55
|
+
// Write-through to LinkCache. transactionResponse is a singular field.
|
|
56
|
+
const persisted = response.getTransactionResponse();
|
|
57
|
+
if (persisted) {
|
|
58
|
+
const uuidProto = persisted.getUuid();
|
|
59
|
+
const asOfProto = persisted.getAsOf();
|
|
60
|
+
if (uuidProto && asOfProto) {
|
|
61
|
+
const uuidKey = UUID.fromU8Array(uuidProto.getRawUuid_asU8()).toString();
|
|
62
|
+
LinkCacheModule.TRANSACTION.put(uuidKey, persisted, new ZonedDateTime(asOfProto));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
52
65
|
return response;
|
|
53
66
|
}
|
|
54
67
|
|
|
@@ -20,15 +20,21 @@ import { ZonedDateTime } from '../models/utils/datetime';
|
|
|
20
20
|
* Write semantics (`put`): newest-vintage wins. An older-vintage put does
|
|
21
21
|
* not evict a newer cached entry.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* Eviction: bounded LRU. When `put` causes the map to exceed `maxEntries`,
|
|
24
|
+
* the least-recently-used entry is removed. `get` bumps recency.
|
|
25
|
+
*
|
|
26
|
+
* Per-entity singletons SECURITY/PORTFOLIO/PRICE/TRANSACTION are tuned
|
|
27
|
+
* for the typical access pattern of each entity (Portfolio + Security
|
|
28
|
+
* change slowly so TTL is 1 day; Price + Transaction change quickly so
|
|
29
|
+
* TTL is short).
|
|
26
30
|
*/
|
|
27
31
|
export declare const DEFAULT_TTL_FOR_LATEST_MS = 600000;
|
|
32
|
+
export declare const DEFAULT_MAX_ENTRIES = 10000;
|
|
28
33
|
export declare class LinkCache<V> {
|
|
29
34
|
private ttlForLatestMs;
|
|
35
|
+
private maxEntries;
|
|
30
36
|
private map;
|
|
31
|
-
constructor(ttlForLatestMs?: number);
|
|
37
|
+
constructor(ttlForLatestMs?: number, maxEntries?: number);
|
|
32
38
|
/**
|
|
33
39
|
* @param uuidKey the entity uuid rendered as a stable string key
|
|
34
40
|
* (use uuid.toString())
|
|
@@ -40,9 +46,10 @@ export declare class LinkCache<V> {
|
|
|
40
46
|
get(uuidKey: string, requestedAsOf: ZonedDateTime | null): V | undefined;
|
|
41
47
|
/**
|
|
42
48
|
* Newest-wins write: if a cached entry for `uuidKey` already exists with
|
|
43
|
-
* an asOf strictly after the incoming asOf, the write is ignored
|
|
49
|
+
* an asOf strictly after the incoming asOf, the write is ignored (but
|
|
50
|
+
* recency is still bumped — the caller saw a fresh reference).
|
|
44
51
|
*/
|
|
45
|
-
put(uuidKey: string, value: V, asOf: ZonedDateTime): void;
|
|
52
|
+
put(uuidKey: string, value: V, asOf: ZonedDateTime | null): void;
|
|
46
53
|
evict(uuidKey: string): void;
|
|
47
54
|
clear(): void;
|
|
48
55
|
/** Test helper. */
|