@fintekkers/ledger-models 0.1.130 → 0.1.132

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.
Files changed (134) hide show
  1. package/node/fintekkers/models/portfolio/portfolio_pb.js +19 -13
  2. package/node/fintekkers/models/position/field_pb.js +7 -1
  3. package/node/fintekkers/models/position/measure_pb.js +7 -1
  4. package/node/fintekkers/models/position/position_filter_pb.js +13 -7
  5. package/node/fintekkers/models/position/position_pb.js +17 -11
  6. package/node/fintekkers/models/position/position_status_pb.js +7 -1
  7. package/node/fintekkers/models/position/position_util_pb.js +17 -11
  8. package/node/fintekkers/models/price/price_pb.js +20 -14
  9. package/node/fintekkers/models/price/price_type_pb.js +7 -1
  10. package/node/fintekkers/models/security/bond/auction_type_pb.js +7 -1
  11. package/node/fintekkers/models/security/bond/issuance_pb.js +23 -17
  12. package/node/fintekkers/models/security/coupon_frequency_pb.js +7 -1
  13. package/node/fintekkers/models/security/coupon_type_pb.js +7 -1
  14. package/node/fintekkers/models/security/identifier/identifier_pb.js +15 -9
  15. package/node/fintekkers/models/security/identifier/identifier_type_pb.js +7 -1
  16. package/node/fintekkers/models/security/index/index_type_pb.js +7 -1
  17. package/node/fintekkers/models/security/index_composition_grpc_pb.js +1 -0
  18. package/node/fintekkers/models/security/index_composition_pb.js +29 -23
  19. package/node/fintekkers/models/security/security_pb.js +96 -90
  20. package/node/fintekkers/models/security/security_quantity_type_pb.js +7 -1
  21. package/node/fintekkers/models/security/security_type_pb.js +7 -1
  22. package/node/fintekkers/models/security/tenor_pb.js +15 -9
  23. package/node/fintekkers/models/security/tenor_type_pb.js +7 -1
  24. package/node/fintekkers/models/strategy/strategy_allocation_pb.js +19 -13
  25. package/node/fintekkers/models/strategy/strategy_pb.js +20 -14
  26. package/node/fintekkers/models/transaction/transaction_pb.js +30 -24
  27. package/node/fintekkers/models/transaction/transaction_type_pb.js +7 -1
  28. package/node/fintekkers/models/util/api/api_key_pb.js +16 -10
  29. package/node/fintekkers/models/util/currency_grpc_pb.js +1 -0
  30. package/node/fintekkers/models/util/currency_pb.js +10 -4
  31. package/node/fintekkers/models/util/date_range_pb.js +14 -8
  32. package/node/fintekkers/models/util/decimal_value_pb.js +10 -4
  33. package/node/fintekkers/models/util/endpoint_pb.js +13 -7
  34. package/node/fintekkers/models/util/local_date_pb.js +11 -5
  35. package/node/fintekkers/models/util/local_timestamp_pb.js +11 -5
  36. package/node/fintekkers/models/util/lock/node_partition_pb.js +15 -9
  37. package/node/fintekkers/models/util/lock/node_state_pb.js +16 -10
  38. package/node/fintekkers/models/util/uuid_pb.js +9 -3
  39. package/node/fintekkers/models/valuation/cashflow_grpc_pb.js +1 -0
  40. package/node/fintekkers/models/valuation/cashflow_pb.js +13 -7
  41. package/node/fintekkers/requests/index_composition/create_index_composition_request_grpc_pb.js +1 -0
  42. package/node/fintekkers/requests/index_composition/create_index_composition_request_pb.js +22 -16
  43. package/node/fintekkers/requests/index_composition/get_index_composition_request_grpc_pb.js +1 -0
  44. package/node/fintekkers/requests/index_composition/get_index_composition_request_pb.js +22 -16
  45. package/node/fintekkers/requests/portfolio/create_portfolio_request_pb.js +13 -7
  46. package/node/fintekkers/requests/portfolio/create_portfolio_response_pb.js +14 -8
  47. package/node/fintekkers/requests/portfolio/query_portfolio_request_pb.js +17 -11
  48. package/node/fintekkers/requests/portfolio/query_portfolio_response_pb.js +14 -8
  49. package/node/fintekkers/requests/position/query_position_request_pb.js +27 -15
  50. package/node/fintekkers/requests/position/query_position_response_pb.js +16 -10
  51. package/node/fintekkers/requests/price/create_price_request_pb.js +13 -7
  52. package/node/fintekkers/requests/price/create_price_response_pb.js +14 -8
  53. package/node/fintekkers/requests/price/query_price_request_pb.js +18 -12
  54. package/node/fintekkers/requests/price/query_price_response_pb.js +14 -8
  55. package/node/fintekkers/requests/security/create_security_request_pb.js +13 -7
  56. package/node/fintekkers/requests/security/create_security_response_pb.js +15 -9
  57. package/node/fintekkers/requests/security/get_field_values_request_pb.js +13 -7
  58. package/node/fintekkers/requests/security/get_field_values_response_pb.js +13 -7
  59. package/node/fintekkers/requests/security/get_fields_response_pb.js +17 -8
  60. package/node/fintekkers/requests/security/query_security_request_pb.js +17 -11
  61. package/node/fintekkers/requests/security/query_security_response_pb.js +15 -9
  62. package/node/fintekkers/requests/transaction/create_transaction_request_pb.js +13 -7
  63. package/node/fintekkers/requests/transaction/create_transaction_response_pb.js +14 -8
  64. package/node/fintekkers/requests/transaction/query_transaction_request_pb.js +16 -10
  65. package/node/fintekkers/requests/transaction/query_transaction_response_pb.js +15 -9
  66. package/node/fintekkers/requests/util/delete_request_grpc_pb.js +1 -0
  67. package/node/fintekkers/requests/util/delete_request_pb.js +34 -28
  68. package/node/fintekkers/requests/util/errors/error_pb.js +13 -7
  69. package/node/fintekkers/requests/util/errors/message_pb.js +12 -6
  70. package/node/fintekkers/requests/util/errors/summary_pb.js +10 -4
  71. package/node/fintekkers/requests/util/lock/lock_request_pb.js +14 -8
  72. package/node/fintekkers/requests/util/lock/lock_response_pb.js +15 -9
  73. package/node/fintekkers/requests/util/operation_pb.js +7 -1
  74. package/node/fintekkers/requests/valuation/curve_request_grpc_pb.js +1 -0
  75. package/node/fintekkers/requests/valuation/curve_request_pb.d.ts +12 -0
  76. package/node/fintekkers/requests/valuation/curve_request_pb.js +125 -14
  77. package/node/fintekkers/requests/valuation/curve_response_grpc_pb.js +1 -0
  78. package/node/fintekkers/requests/valuation/curve_response_pb.js +21 -15
  79. package/node/fintekkers/requests/valuation/product_inputs.test.d.ts +6 -0
  80. package/node/fintekkers/requests/valuation/product_inputs.test.js +146 -0
  81. package/node/fintekkers/requests/valuation/product_inputs.test.js.map +1 -0
  82. package/node/fintekkers/requests/valuation/product_inputs_grpc_pb.js +1 -0
  83. package/node/fintekkers/requests/valuation/product_inputs_pb.d.ts +42 -0
  84. package/node/fintekkers/requests/valuation/product_inputs_pb.js +360 -27
  85. package/node/fintekkers/requests/valuation/valuation_request_pb.js +25 -16
  86. package/node/fintekkers/requests/valuation/valuation_response_pb.js +16 -10
  87. package/node/fintekkers/services/index-composition-service/index_composition_service_grpc_pb.js +14 -14
  88. package/node/fintekkers/services/index-composition-service/index_composition_service_pb.js +7 -1
  89. package/node/fintekkers/services/lock-service/lock_service_grpc_pb.js +23 -23
  90. package/node/fintekkers/services/lock-service/lock_service_pb.js +21 -15
  91. package/node/fintekkers/services/portfolio-service/portfolio_service_grpc_pb.js +8 -8
  92. package/node/fintekkers/services/portfolio-service/portfolio_service_pb.js +7 -1
  93. package/node/fintekkers/services/position-service/position_service_grpc_pb.js +10 -10
  94. package/node/fintekkers/services/position-service/position_service_pb.js +7 -1
  95. package/node/fintekkers/services/price-service/price_service_grpc_pb.js +6 -6
  96. package/node/fintekkers/services/price-service/price_service_pb.js +7 -1
  97. package/node/fintekkers/services/security-service/security_service_grpc_pb.js +12 -12
  98. package/node/fintekkers/services/security-service/security_service_pb.js +7 -1
  99. package/node/fintekkers/services/transaction-service/transaction_service_grpc_pb.js +11 -11
  100. package/node/fintekkers/services/transaction-service/transaction_service_pb.js +7 -1
  101. package/node/fintekkers/services/valuation-service/valuation_service_grpc_pb.js +5 -5
  102. package/node/fintekkers/services/valuation-service/valuation_service_pb.js +7 -1
  103. package/node/wrappers/models/price/Price.d.ts +5 -0
  104. package/node/wrappers/models/price/Price.js +7 -0
  105. package/node/wrappers/models/price/Price.js.map +1 -1
  106. package/node/wrappers/models/price/Price.ts +8 -0
  107. package/node/wrappers/models/security/security.d.ts +6 -0
  108. package/node/wrappers/models/security/security.js +8 -0
  109. package/node/wrappers/models/security/security.js.map +1 -1
  110. package/node/wrappers/models/security/security.ts +9 -0
  111. package/node/wrappers/services/price-service/PriceService.d.ts +19 -0
  112. package/node/wrappers/services/price-service/PriceService.js +26 -0
  113. package/node/wrappers/services/price-service/PriceService.js.map +1 -1
  114. package/node/wrappers/services/price-service/PriceService.ts +29 -0
  115. package/node/wrappers/services/searchWithSecurities.test.js +125 -0
  116. package/node/wrappers/services/searchWithSecurities.test.js.map +1 -0
  117. package/node/wrappers/services/searchWithSecurities.test.ts +103 -0
  118. package/node/wrappers/services/transaction-service/TransactionService.d.ts +14 -0
  119. package/node/wrappers/services/transaction-service/TransactionService.js +25 -0
  120. package/node/wrappers/services/transaction-service/TransactionService.js.map +1 -1
  121. package/node/wrappers/services/transaction-service/TransactionService.ts +29 -0
  122. package/node/wrappers/util/link-resolver.d.ts +127 -0
  123. package/node/wrappers/util/link-resolver.js +378 -0
  124. package/node/wrappers/util/link-resolver.js.map +1 -0
  125. package/node/wrappers/util/link-resolver.test.d.ts +1 -0
  126. package/node/wrappers/util/link-resolver.test.js +349 -0
  127. package/node/wrappers/util/link-resolver.test.js.map +1 -0
  128. package/node/wrappers/util/link-resolver.test.ts +402 -0
  129. package/node/wrappers/util/link-resolver.ts +448 -0
  130. package/package.json +1 -1
  131. package/node/wrappers/services/security-service/SecurityService.searchByUuid.test.js +0 -38
  132. package/node/wrappers/services/security-service/SecurityService.searchByUuid.test.js.map +0 -1
  133. package/node/wrappers/services/security-service/SecurityService.searchByUuid.test.ts +0 -32
  134. /package/node/wrappers/services/{security-service/SecurityService.searchByUuid.test.d.ts → searchWithSecurities.test.d.ts} +0 -0
@@ -0,0 +1,125 @@
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
+ };
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
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ const PriceService_1 = require("./price-service/PriceService");
39
+ const link_resolver_1 = __importDefault(require("../util/link-resolver"));
40
+ const Price_1 = __importDefault(require("../models/price/Price"));
41
+ const uuid_1 = require("../models/utils/uuid");
42
+ const positionfilter_1 = require("../models/position/positionfilter");
43
+ const dt = __importStar(require("../models/utils/datetime"));
44
+ const security_pb_1 = require("../../fintekkers/models/security/security_pb");
45
+ const price_pb_1 = require("../../fintekkers/models/price/price_pb");
46
+ const decimal_value_pb_1 = require("../../fintekkers/models/util/decimal_value_pb");
47
+ const identifier_pb_1 = require("../../fintekkers/models/security/identifier/identifier_pb");
48
+ const query_security_response_pb_1 = require("../../fintekkers/requests/security/query_security_response_pb");
49
+ /**
50
+ * End-to-end test: priceService.searchWithSecurities resolves all link
51
+ * securities embedded in the returned Prices via a single batched
52
+ * GetByIds RPC.
53
+ */
54
+ function fullSecurity(uuid, issuerName) {
55
+ const proto = new security_pb_1.SecurityProto();
56
+ proto.setObjectClass('Security');
57
+ proto.setVersion('0.0.1');
58
+ proto.setUuid(uuid.toUUIDProto());
59
+ proto.setIsLink(false);
60
+ proto.setIssuerName(issuerName);
61
+ const ident = new identifier_pb_1.IdentifierProto();
62
+ ident.setIdentifierValue(`TICKER-${issuerName}`);
63
+ proto.setIdentifier(ident);
64
+ return proto;
65
+ }
66
+ function linkPriceProto(securityUuid, priceValue) {
67
+ const linkSec = new security_pb_1.SecurityProto();
68
+ linkSec.setUuid(securityUuid.toUUIDProto());
69
+ linkSec.setIsLink(true);
70
+ const priceProto = new price_pb_1.PriceProto();
71
+ priceProto.setObjectClass('Price');
72
+ priceProto.setVersion('0.0.1');
73
+ priceProto.setUuid(uuid_1.UUID.random().toUUIDProto());
74
+ priceProto.setSecurity(linkSec);
75
+ const dv = new decimal_value_pb_1.DecimalValueProto();
76
+ dv.setArbitraryPrecisionValue(priceValue);
77
+ priceProto.setPrice(dv);
78
+ return priceProto;
79
+ }
80
+ test('PriceService.searchWithSecurities returns hydrated Prices end-to-end', () => __awaiter(void 0, void 0, void 0, function* () {
81
+ const uuidA = uuid_1.UUID.random();
82
+ const uuidB = uuid_1.UUID.random();
83
+ const store = new Map([
84
+ [uuidA.toString(), fullSecurity(uuidA, 'AAPL')],
85
+ [uuidB.toString(), fullSecurity(uuidB, 'MSFT')],
86
+ ]);
87
+ const callLog = { count: 0 };
88
+ // Stub LinkResolver with a mock SecurityClient.
89
+ const mockSecurityClient = {
90
+ getByIds: (request, callback) => {
91
+ callLog.count += 1;
92
+ const requested = request.getUuidsList().map((u) => uuid_1.UUID.fromU8Array(u.getRawUuid_asU8()).toString());
93
+ const response = new query_security_response_pb_1.QuerySecurityResponseProto();
94
+ response.setSecurityResponseList(requested.map((u) => store.get(u)).filter(Boolean));
95
+ setImmediate(() => callback(null, response));
96
+ },
97
+ };
98
+ const linkResolver = new link_resolver_1.default({
99
+ securityClient: mockSecurityClient,
100
+ portfolioClient: {},
101
+ });
102
+ // Stub PriceService.search to skip the gRPC stream and return canned Prices.
103
+ const priceService = new PriceService_1.PriceService();
104
+ jest
105
+ .spyOn(priceService, 'search')
106
+ .mockResolvedValue([
107
+ new Price_1.default(linkPriceProto(uuidA, '100')),
108
+ new Price_1.default(linkPriceProto(uuidA, '101')),
109
+ new Price_1.default(linkPriceProto(uuidB, '200')),
110
+ ]);
111
+ const asOf = dt.ZonedDateTime.now().toProto();
112
+ const filter = new positionfilter_1.PositionFilter();
113
+ const prices = yield priceService.searchWithSecurities(asOf, filter, linkResolver);
114
+ expect(prices).toHaveLength(3);
115
+ // 1 batched RPC for 2 unique security UUIDs.
116
+ expect(callLog.count).toBe(1);
117
+ // Every Price now has a hydrated full Security embedded.
118
+ for (const p of prices) {
119
+ const sec = p.proto.getSecurity();
120
+ expect(sec.getIsLink()).toBe(false);
121
+ expect(['AAPL', 'MSFT']).toContain(sec.getIssuerName());
122
+ expect(sec.getIdentifier().getIdentifierValue()).toMatch(/^TICKER-/);
123
+ }
124
+ }));
125
+ //# sourceMappingURL=searchWithSecurities.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"searchWithSecurities.test.js","sourceRoot":"","sources":["searchWithSecurities.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ import { PriceService } from './price-service/PriceService';
2
+ import LinkResolver from '../util/link-resolver';
3
+ import Price from '../models/price/Price';
4
+ import { UUID } from '../models/utils/uuid';
5
+ import { PositionFilter } from '../models/position/positionfilter';
6
+ import * as dt from '../models/utils/datetime';
7
+
8
+ import { SecurityProto } from '../../fintekkers/models/security/security_pb';
9
+ import { PriceProto } from '../../fintekkers/models/price/price_pb';
10
+ import { DecimalValueProto } from '../../fintekkers/models/util/decimal_value_pb';
11
+ import { IdentifierProto } from '../../fintekkers/models/security/identifier/identifier_pb';
12
+ import { QuerySecurityResponseProto } from '../../fintekkers/requests/security/query_security_response_pb';
13
+ import { QuerySecurityRequestProto } from '../../fintekkers/requests/security/query_security_request_pb';
14
+
15
+ /**
16
+ * End-to-end test: priceService.searchWithSecurities resolves all link
17
+ * securities embedded in the returned Prices via a single batched
18
+ * GetByIds RPC.
19
+ */
20
+
21
+ function fullSecurity(uuid: UUID, issuerName: string): SecurityProto {
22
+ const proto = new SecurityProto();
23
+ proto.setObjectClass('Security');
24
+ proto.setVersion('0.0.1');
25
+ proto.setUuid(uuid.toUUIDProto());
26
+ proto.setIsLink(false);
27
+ proto.setIssuerName(issuerName);
28
+ const ident = new IdentifierProto();
29
+ ident.setIdentifierValue(`TICKER-${issuerName}`);
30
+ proto.setIdentifier(ident);
31
+ return proto;
32
+ }
33
+
34
+ function linkPriceProto(securityUuid: UUID, priceValue: string): PriceProto {
35
+ const linkSec = new SecurityProto();
36
+ linkSec.setUuid(securityUuid.toUUIDProto());
37
+ linkSec.setIsLink(true);
38
+
39
+ const priceProto = new PriceProto();
40
+ priceProto.setObjectClass('Price');
41
+ priceProto.setVersion('0.0.1');
42
+ priceProto.setUuid(UUID.random().toUUIDProto());
43
+ priceProto.setSecurity(linkSec);
44
+ const dv = new DecimalValueProto();
45
+ dv.setArbitraryPrecisionValue(priceValue);
46
+ priceProto.setPrice(dv);
47
+ return priceProto;
48
+ }
49
+
50
+ test('PriceService.searchWithSecurities returns hydrated Prices end-to-end', async () => {
51
+ const uuidA = UUID.random();
52
+ const uuidB = UUID.random();
53
+ const store = new Map<string, SecurityProto>([
54
+ [uuidA.toString(), fullSecurity(uuidA, 'AAPL')],
55
+ [uuidB.toString(), fullSecurity(uuidB, 'MSFT')],
56
+ ]);
57
+ const callLog = { count: 0 };
58
+
59
+ // Stub LinkResolver with a mock SecurityClient.
60
+ const mockSecurityClient = {
61
+ getByIds: (
62
+ request: QuerySecurityRequestProto,
63
+ callback: (err: Error | null, response: QuerySecurityResponseProto) => void,
64
+ ) => {
65
+ callLog.count += 1;
66
+ const requested = request.getUuidsList().map((u) => UUID.fromU8Array(u.getRawUuid_asU8()).toString());
67
+ const response = new QuerySecurityResponseProto();
68
+ response.setSecurityResponseList(requested.map((u) => store.get(u)!).filter(Boolean));
69
+ setImmediate(() => callback(null, response));
70
+ },
71
+ } as any;
72
+
73
+ const linkResolver = new LinkResolver({
74
+ securityClient: mockSecurityClient,
75
+ portfolioClient: {} as any,
76
+ });
77
+
78
+ // Stub PriceService.search to skip the gRPC stream and return canned Prices.
79
+ const priceService = new PriceService();
80
+ jest
81
+ .spyOn(priceService, 'search')
82
+ .mockResolvedValue([
83
+ new Price(linkPriceProto(uuidA, '100')),
84
+ new Price(linkPriceProto(uuidA, '101')),
85
+ new Price(linkPriceProto(uuidB, '200')),
86
+ ]);
87
+
88
+ const asOf = dt.ZonedDateTime.now().toProto();
89
+ const filter = new PositionFilter();
90
+
91
+ const prices = await priceService.searchWithSecurities(asOf, filter, linkResolver);
92
+
93
+ expect(prices).toHaveLength(3);
94
+ // 1 batched RPC for 2 unique security UUIDs.
95
+ expect(callLog.count).toBe(1);
96
+ // Every Price now has a hydrated full Security embedded.
97
+ for (const p of prices) {
98
+ const sec = p.proto.getSecurity()!;
99
+ expect(sec.getIsLink()).toBe(false);
100
+ expect(['AAPL', 'MSFT']).toContain(sec.getIssuerName());
101
+ expect(sec.getIdentifier()!.getIdentifierValue()).toMatch(/^TICKER-/);
102
+ }
103
+ });
@@ -3,11 +3,25 @@ import { PositionFilter } from '../../models/position/positionfilter';
3
3
  import { LocalTimestampProto } from '../../../fintekkers/models/util/local_timestamp_pb';
4
4
  import { SummaryProto } from '../../../fintekkers/requests/util/errors/summary_pb';
5
5
  import { CreateTransactionResponseProto } from '../../../fintekkers/requests/transaction/create_transaction_response_pb';
6
+ import LinkResolver from '../../util/link-resolver';
6
7
  declare class TransactionService {
7
8
  private client;
8
9
  constructor(apiKey?: string);
9
10
  validateCreateTransaction(transaction: Transaction): Promise<SummaryProto>;
10
11
  createTransaction(transaction: Transaction): Promise<CreateTransactionResponseProto>;
11
12
  searchTransaction(asOf: LocalTimestampProto, positionFilter: PositionFilter, maxResults?: number): Promise<Transaction[]>;
13
+ /**
14
+ * Search transactions and hydrate each Transaction's embedded Security
15
+ * AND Portfolio from link to full entity, with both fetches batched.
16
+ *
17
+ * Pass a shared `linkResolver` to share caching across multiple
18
+ * service-wrapper calls in the same request scope. If omitted, a new
19
+ * resolver is constructed per call.
20
+ *
21
+ * Mutates each returned Transaction.proto's embedded SecurityProto and
22
+ * PortfolioProto in place (link → full). See LinkResolver for cache +
23
+ * dedupe semantics.
24
+ */
25
+ searchWithSecurityAndPortfolio(asOf: LocalTimestampProto, positionFilter: PositionFilter, maxResults?: number, linkResolver?: LinkResolver): Promise<Transaction[]>;
12
26
  }
13
27
  export { TransactionService };
@@ -22,6 +22,7 @@ const transaction_service_grpc_pb_1 = require("../../../fintekkers/services/tran
22
22
  const create_transaction_request_pb_1 = require("../../../fintekkers/requests/transaction/create_transaction_request_pb");
23
23
  const query_transaction_request_pb_1 = require("../../../fintekkers/requests/transaction/query_transaction_request_pb");
24
24
  const requestcontext_1 = __importDefault(require("../../models/utils/requestcontext"));
25
+ const link_resolver_1 = __importDefault(require("../../util/link-resolver"));
25
26
  class TransactionService {
26
27
  constructor(apiKey) {
27
28
  if (apiKey) {
@@ -86,6 +87,30 @@ class TransactionService {
86
87
  }
87
88
  return processStreamSynchronously();
88
89
  }
90
+ /**
91
+ * Search transactions and hydrate each Transaction's embedded Security
92
+ * AND Portfolio from link to full entity, with both fetches batched.
93
+ *
94
+ * Pass a shared `linkResolver` to share caching across multiple
95
+ * service-wrapper calls in the same request scope. If omitted, a new
96
+ * resolver is constructed per call.
97
+ *
98
+ * Mutates each returned Transaction.proto's embedded SecurityProto and
99
+ * PortfolioProto in place (link → full). See LinkResolver for cache +
100
+ * dedupe semantics.
101
+ */
102
+ searchWithSecurityAndPortfolio(asOf, positionFilter, maxResults = 100, linkResolver) {
103
+ return __awaiter(this, void 0, void 0, function* () {
104
+ const txns = yield this.searchTransaction(asOf, positionFilter, maxResults);
105
+ const resolver = linkResolver !== null && linkResolver !== void 0 ? linkResolver : new link_resolver_1.default();
106
+ // Run both resolves in parallel — they hit different services.
107
+ yield Promise.all([
108
+ resolver.resolveSecurities(txns),
109
+ resolver.resolvePortfolios(txns),
110
+ ]);
111
+ return txns;
112
+ });
113
+ }
89
114
  }
90
115
  exports.TransactionService = TransactionService;
91
116
  //# sourceMappingURL=TransactionService.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TransactionService.js","sourceRoot":"","sources":["TransactionService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TransactionService.js","sourceRoot":"","sources":["TransactionService.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,7 @@ import { CreateTransactionResponseProto } from '../../../fintekkers/requests/tra
15
15
  import { QueryTransactionRequestProto } from '../../../fintekkers/requests/transaction/query_transaction_request_pb';
16
16
  import { QueryTransactionResponseProto } from '../../../fintekkers/requests/transaction/query_transaction_response_pb';
17
17
  import EnvConfig from '../../models/utils/requestcontext';
18
+ import LinkResolver from '../../util/link-resolver';
18
19
 
19
20
 
20
21
  class TransactionService {
@@ -89,6 +90,34 @@ class TransactionService {
89
90
 
90
91
  return processStreamSynchronously();
91
92
  }
93
+
94
+ /**
95
+ * Search transactions and hydrate each Transaction's embedded Security
96
+ * AND Portfolio from link to full entity, with both fetches batched.
97
+ *
98
+ * Pass a shared `linkResolver` to share caching across multiple
99
+ * service-wrapper calls in the same request scope. If omitted, a new
100
+ * resolver is constructed per call.
101
+ *
102
+ * Mutates each returned Transaction.proto's embedded SecurityProto and
103
+ * PortfolioProto in place (link → full). See LinkResolver for cache +
104
+ * dedupe semantics.
105
+ */
106
+ async searchWithSecurityAndPortfolio(
107
+ asOf: LocalTimestampProto,
108
+ positionFilter: PositionFilter,
109
+ maxResults: number = 100,
110
+ linkResolver?: LinkResolver,
111
+ ): Promise<Transaction[]> {
112
+ const txns = await this.searchTransaction(asOf, positionFilter, maxResults);
113
+ const resolver = linkResolver ?? new LinkResolver();
114
+ // Run both resolves in parallel — they hit different services.
115
+ await Promise.all([
116
+ resolver.resolveSecurities(txns),
117
+ resolver.resolvePortfolios(txns),
118
+ ]);
119
+ return txns;
120
+ }
92
121
  }
93
122
 
94
123
  export { TransactionService };
@@ -0,0 +1,127 @@
1
+ import { SecurityProto } from '../../fintekkers/models/security/security_pb';
2
+ import { PortfolioProto } from '../../fintekkers/models/portfolio/portfolio_pb';
3
+ import { LocalTimestampProto } from '../../fintekkers/models/util/local_timestamp_pb';
4
+ import { SecurityClient } from '../../fintekkers/services/security-service/security_service_grpc_pb';
5
+ import { PortfolioClient } from '../../fintekkers/services/portfolio-service/portfolio_service_grpc_pb';
6
+ import Security from '../models/security/security';
7
+ import Portfolio from '../models/portfolio/portfolio';
8
+ import { UUID } from '../models/utils/uuid';
9
+ /**
10
+ * LinkResolver — bulk hydration of `is_link=true` entity references into
11
+ * full entities. Implements the consumer side of the `is_link` pattern
12
+ * documented in `docs/adr/is_link_pattern.md`.
13
+ *
14
+ * Two surface methods:
15
+ * - getSecurity(uuid) / getPortfolio(uuid): single-UUID resolution. Cached
16
+ * and concurrent-deduped.
17
+ * - resolveSecurities(items) / resolvePortfolios(items): bulk in-place
18
+ * mutation across a heterogeneous list of items that each have a
19
+ * proto-style getter+setter for the embedded entity. Collects unique
20
+ * link UUIDs, fires one batched GetByIds RPC, mutates each item's proto
21
+ * to swap the link sub-message for the resolved full entity (with
22
+ * is_link=false on the embedded copy).
23
+ *
24
+ * Caching:
25
+ * - Process-level LRU keyed on UUID string. Default 1000 entries, no TTL
26
+ * (entries live until evicted by LRU). Long-running services that need
27
+ * freshness should pass `{ ttlMs: <ms> }`. Tests can disable with
28
+ * `{ cacheSize: 0 }`.
29
+ * - Concurrent same-UUID requests are deduped via an in-flight promise
30
+ * map — N parallel callers for the same UUID share one RPC.
31
+ *
32
+ * RPC choice: uses `GetByIds` (unary, UUID-keyed bulk) per the ADR. The
33
+ * existing `SecurityService.search` (streaming) would also work but
34
+ * requires more wrapper plumbing for batched-by-UUID semantics.
35
+ *
36
+ * Mutation semantic: when bulk-resolving, the embedded sub-message is
37
+ * replaced (not the outer entity). Outer Price.proto.is_link is unchanged;
38
+ * only the inner SecurityProto is swapped from link-stub to full entity.
39
+ * Wrapper objects that read through the proto (`price.getSecurity()`)
40
+ * automatically see the resolved data.
41
+ *
42
+ * Time-travel (`as_of`) semantic: per is_link_pattern.md addendum, when
43
+ * a link sub-message has only `uuid` set the resolver fetches the latest
44
+ * version. When the link sub-message ALSO has `as_of` set, the resolver
45
+ * fetches the version of the entity as of that timestamp. The cache is
46
+ * keyed on (uuid, as_of) so the same UUID at different timestamps does
47
+ * not collide. Bulk lookups group by `as_of` (one GetByIds RPC per unique
48
+ * timestamp bucket, since the request proto carries a single as_of).
49
+ */
50
+ export interface LinkResolverOptions {
51
+ /** Optional API key. If omitted, EnvConfig.apiCredentials is used. */
52
+ apiKey?: string;
53
+ /** LRU max entries. Default 1000. Set to 0 to disable caching. */
54
+ cacheSize?: number;
55
+ /** Per-entry TTL in ms. Default undefined (no expiry). */
56
+ ttlMs?: number;
57
+ /**
58
+ * Test injection: clients to use instead of constructing real ones.
59
+ * Production callers should not set these.
60
+ */
61
+ securityClient?: SecurityClient;
62
+ portfolioClient?: PortfolioClient;
63
+ }
64
+ declare class LinkResolver {
65
+ private securityClient;
66
+ private portfolioClient;
67
+ private securityCache;
68
+ private portfolioCache;
69
+ private securityInFlight;
70
+ private portfolioInFlight;
71
+ constructor(opts?: LinkResolverOptions);
72
+ /**
73
+ * Resolve a single SecurityProto by UUID. If `asOf` is supplied, fetch
74
+ * the version of the entity as of that timestamp; otherwise fetch the
75
+ * latest. Cached + concurrent-deduped on the (uuid, asOf) pair.
76
+ * Throws if the server doesn't return the UUID (no silent null).
77
+ */
78
+ getSecurity(uuid: UUID, asOf?: LocalTimestampProto): Promise<Security>;
79
+ /**
80
+ * Resolve a single PortfolioProto by UUID, optionally as of `asOf`.
81
+ * Cached + concurrent-deduped on (uuid, asOf).
82
+ */
83
+ getPortfolio(uuid: UUID, asOf?: LocalTimestampProto): Promise<Portfolio>;
84
+ /**
85
+ * Walk `items`, find the ones whose embedded security is `is_link=true`,
86
+ * batch-fetch the unique (uuid, as_of) pairs (grouped by as_of so each
87
+ * GetByIds RPC carries one timestamp), and mutate each item's proto in
88
+ * place so subsequent `item.getSecurity()` calls return the full entity.
89
+ * Returns the same array for chaining.
90
+ *
91
+ * Honors per-link `as_of`: if the embedded sub-message has `as_of` set,
92
+ * the resolver fetches the version of the entity at that timestamp,
93
+ * not the latest.
94
+ *
95
+ * `T` is structural: anything with a `proto` field that exposes
96
+ * `getSecurity()` / `setSecurity()` works (Price, Transaction, etc).
97
+ */
98
+ resolveSecurities<T extends ResolvableSecurity>(items: T[]): Promise<T[]>;
99
+ /**
100
+ * Same shape as resolveSecurities, but for embedded PortfolioProto.
101
+ * Honors per-link `as_of` the same way.
102
+ */
103
+ resolvePortfolios<T extends ResolvablePortfolio>(items: T[]): Promise<T[]>;
104
+ /** Test/debug helper. Not part of the stable API. */
105
+ clearCache(): void;
106
+ private fetchSecurityProto;
107
+ private fetchPortfolioProto;
108
+ private batchFetchSecurities;
109
+ private batchFetchPortfolios;
110
+ }
111
+ /**
112
+ * Structural type — anything with a proto that has getSecurity/setSecurity
113
+ * (Price, Transaction, etc.) is resolvable.
114
+ */
115
+ export interface ResolvableSecurity {
116
+ proto: {
117
+ getSecurity(): SecurityProto | undefined;
118
+ setSecurity(s: SecurityProto): unknown;
119
+ };
120
+ }
121
+ export interface ResolvablePortfolio {
122
+ proto: {
123
+ getPortfolio(): PortfolioProto | undefined;
124
+ setPortfolio(p: PortfolioProto): unknown;
125
+ };
126
+ }
127
+ export default LinkResolver;