@ductape/sdk 0.1.11 → 0.1.13

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 (117) hide show
  1. package/dist/api/services/cloudApi.service.d.ts +8 -8
  2. package/dist/api/services/cloudApi.service.js +30 -12
  3. package/dist/api/services/cloudApi.service.js.map +1 -1
  4. package/dist/api/services/webhooksApi.service.js +6 -4
  5. package/dist/api/services/webhooksApi.service.js.map +1 -1
  6. package/dist/api/urls.d.ts +2 -0
  7. package/dist/api/urls.js +3 -1
  8. package/dist/api/urls.js.map +1 -1
  9. package/dist/apps/services/app.service.d.ts +4 -0
  10. package/dist/apps/services/app.service.js +37 -0
  11. package/dist/apps/services/app.service.js.map +1 -1
  12. package/dist/brokers/brokers.service.js +23 -1
  13. package/dist/brokers/brokers.service.js.map +1 -1
  14. package/dist/brokers/utils/broker.util.js +4 -1
  15. package/dist/brokers/utils/broker.util.js.map +1 -1
  16. package/dist/brokers/utils/providers/azure-servicebus.service.d.ts +17 -0
  17. package/dist/brokers/utils/providers/azure-servicebus.service.js +51 -0
  18. package/dist/brokers/utils/providers/azure-servicebus.service.js.map +1 -0
  19. package/dist/cloud/cloud-api.util.d.ts +4 -0
  20. package/dist/cloud/cloud-api.util.js +27 -0
  21. package/dist/cloud/cloud-api.util.js.map +1 -0
  22. package/dist/cloud/cloud-broker-link.util.d.ts +9 -0
  23. package/dist/cloud/cloud-broker-link.util.js +83 -0
  24. package/dist/cloud/cloud-broker-link.util.js.map +1 -0
  25. package/dist/cloud/cloud-database-link.util.d.ts +12 -0
  26. package/dist/cloud/cloud-database-link.util.js +62 -0
  27. package/dist/cloud/cloud-database-link.util.js.map +1 -0
  28. package/dist/cloud/cloud-graph-link.util.d.ts +12 -0
  29. package/dist/cloud/cloud-graph-link.util.js +61 -0
  30. package/dist/cloud/cloud-graph-link.util.js.map +1 -0
  31. package/dist/cloud/cloud-runtime.util.js +21 -9
  32. package/dist/cloud/cloud-runtime.util.js.map +1 -1
  33. package/dist/cloud/cloud-service-map.util.d.ts +3 -0
  34. package/dist/cloud/cloud-service-map.util.js +52 -0
  35. package/dist/cloud/cloud-service-map.util.js.map +1 -0
  36. package/dist/cloud/cloud-vector-link.util.d.ts +12 -0
  37. package/dist/cloud/cloud-vector-link.util.js +60 -0
  38. package/dist/cloud/cloud-vector-link.util.js.map +1 -0
  39. package/dist/cloud/cloud.service.d.ts +11 -16
  40. package/dist/cloud/cloud.service.js +10 -14
  41. package/dist/cloud/cloud.service.js.map +1 -1
  42. package/dist/cloud/index.d.ts +6 -0
  43. package/dist/cloud/index.js +14 -1
  44. package/dist/cloud/index.js.map +1 -1
  45. package/dist/cloud/types/cloud.types.d.ts +22 -7
  46. package/dist/database/databases.service.js +2 -0
  47. package/dist/database/databases.service.js.map +1 -1
  48. package/dist/database/types/connection.interface.d.ts +4 -0
  49. package/dist/graph/adapters/adapter.factory.js +4 -0
  50. package/dist/graph/adapters/adapter.factory.js.map +1 -1
  51. package/dist/graph/adapters/cosmos-gremlin.adapter.d.ts +12 -0
  52. package/dist/graph/adapters/cosmos-gremlin.adapter.js +125 -0
  53. package/dist/graph/adapters/cosmos-gremlin.adapter.js.map +1 -0
  54. package/dist/graph/adapters/index.d.ts +2 -0
  55. package/dist/graph/adapters/index.js +5 -1
  56. package/dist/graph/adapters/index.js.map +1 -1
  57. package/dist/graph/adapters/neptune.adapter.d.ts +4 -4
  58. package/dist/graph/adapters/neptune.adapter.js.map +1 -1
  59. package/dist/graph/adapters/spanner-graph.adapter.d.ts +68 -0
  60. package/dist/graph/adapters/spanner-graph.adapter.js +220 -0
  61. package/dist/graph/adapters/spanner-graph.adapter.js.map +1 -0
  62. package/dist/graph/types/enums.d.ts +2 -0
  63. package/dist/graph/types/enums.js +2 -0
  64. package/dist/graph/types/enums.js.map +1 -1
  65. package/dist/index.d.ts +20 -5
  66. package/dist/index.js +30 -9
  67. package/dist/index.js.map +1 -1
  68. package/dist/logs/logs.types.d.ts +5 -1
  69. package/dist/logs/logs.types.js +2 -0
  70. package/dist/logs/logs.types.js.map +1 -1
  71. package/dist/products/services/products.service.d.ts +14 -0
  72. package/dist/products/services/products.service.js +296 -118
  73. package/dist/products/services/products.service.js.map +1 -1
  74. package/dist/products/validators/joi-validators/create.productDatabase.validator.js +22 -17
  75. package/dist/products/validators/joi-validators/create.productDatabase.validator.js.map +1 -1
  76. package/dist/products/validators/joi-validators/create.productGraph.validator.js +8 -0
  77. package/dist/products/validators/joi-validators/create.productGraph.validator.js.map +1 -1
  78. package/dist/products/validators/joi-validators/create.productMessageBrokers.validator.js +51 -9
  79. package/dist/products/validators/joi-validators/create.productMessageBrokers.validator.js.map +1 -1
  80. package/dist/products/validators/joi-validators/create.productStorage.validator.js +39 -26
  81. package/dist/products/validators/joi-validators/create.productStorage.validator.js.map +1 -1
  82. package/dist/products/validators/joi-validators/create.productVector.validator.js +8 -0
  83. package/dist/products/validators/joi-validators/create.productVector.validator.js.map +1 -1
  84. package/dist/products/validators/joi-validators/update.productDatabase.validator.js +4 -0
  85. package/dist/products/validators/joi-validators/update.productDatabase.validator.js.map +1 -1
  86. package/dist/resilience/fallback.service.js +11 -6
  87. package/dist/resilience/fallback.service.js.map +1 -1
  88. package/dist/resilience/quota.service.d.ts +1 -1
  89. package/dist/resilience/quota.service.js +48 -10
  90. package/dist/resilience/quota.service.js.map +1 -1
  91. package/dist/resilience/resilience-mapping.utils.d.ts +5 -0
  92. package/dist/resilience/resilience-mapping.utils.js +53 -0
  93. package/dist/resilience/resilience-mapping.utils.js.map +1 -0
  94. package/dist/storage/storage-cloud-link.util.d.ts +13 -0
  95. package/dist/storage/storage-cloud-link.util.js +92 -0
  96. package/dist/storage/storage-cloud-link.util.js.map +1 -0
  97. package/dist/types/productsBuilder.types.d.ts +17 -0
  98. package/dist/types/productsBuilder.types.js +6 -0
  99. package/dist/types/productsBuilder.types.js.map +1 -1
  100. package/dist/vector/adapters/azure-search.adapter.d.ts +25 -0
  101. package/dist/vector/adapters/azure-search.adapter.js +217 -0
  102. package/dist/vector/adapters/azure-search.adapter.js.map +1 -0
  103. package/dist/vector/adapters/index.d.ts +3 -0
  104. package/dist/vector/adapters/index.js +7 -1
  105. package/dist/vector/adapters/index.js.map +1 -1
  106. package/dist/vector/adapters/opensearch.adapter.d.ts +25 -0
  107. package/dist/vector/adapters/opensearch.adapter.js +224 -0
  108. package/dist/vector/adapters/opensearch.adapter.js.map +1 -0
  109. package/dist/vector/adapters/vertex-vector-search.adapter.d.ts +29 -0
  110. package/dist/vector/adapters/vertex-vector-search.adapter.js +178 -0
  111. package/dist/vector/adapters/vertex-vector-search.adapter.js.map +1 -0
  112. package/dist/vector/types/enums.d.ts +6 -0
  113. package/dist/vector/types/enums.js +6 -0
  114. package/dist/vector/types/enums.js.map +1 -1
  115. package/dist/vector/vector.service.js +6 -0
  116. package/dist/vector/vector.service.js.map +1 -1
  117. package/package.json +8 -2
@@ -21,6 +21,12 @@ const userApi_service_1 = require("../../api/services/userApi.service");
21
21
  const inputs_service_1 = __importDefault(require("../../inputs/inputs.service"));
22
22
  const inputs_utils_create_1 = require("../../inputs/utils/inputs.utils.create");
23
23
  const enums_1 = require("../../types/enums");
24
+ const cloudApi_service_1 = require("../../api/services/cloudApi.service");
25
+ const storage_cloud_link_util_1 = require("../../storage/storage-cloud-link.util");
26
+ const cloud_broker_link_util_1 = require("../../cloud/cloud-broker-link.util");
27
+ const cloud_database_link_util_1 = require("../../cloud/cloud-database-link.util");
28
+ const cloud_graph_link_util_1 = require("../../cloud/cloud-graph-link.util");
29
+ const cloud_vector_link_util_1 = require("../../cloud/cloud-vector-link.util");
24
30
  const productsBuilder_types_1 = require("../../types/productsBuilder.types");
25
31
  const validators_1 = require("../validators");
26
32
  const objects_utils_1 = require("../utils/objects.utils");
@@ -59,6 +65,7 @@ class ProductsBuilderService {
59
65
  this.token = token;
60
66
  this.access_key = access_key || null;
61
67
  this.workspace_private_key = workspace_private_key !== null && workspace_private_key !== void 0 ? workspace_private_key : null;
68
+ this.env_type = env_type;
62
69
  this.userApi = new userApi_service_1.UserApiService(env_type, redis_client);
63
70
  this.productApi = new productsApi_service_1.ProductsApiService(env_type, redis_client);
64
71
  this.workspaceApi = new workspaceApi_service_1.WorkspaceApiService(env_type, redis_client);
@@ -215,6 +222,34 @@ class ProductsBuilderService {
215
222
  const healthchecks = await this.productApi.fetchProductComponents(this.product_id, 'healthcheck', this.getUserAccess());
216
223
  return healthchecks;
217
224
  }
225
+ /** Validate quota/fallback provider references a registered product healthcheck component. */
226
+ async validateProviderHealthcheckTag(option) {
227
+ if (!option.healthcheck)
228
+ return;
229
+ const healthchecks = await this.fetchProductHealthchecks();
230
+ const found = healthchecks.some((h) => h.tag === option.healthcheck);
231
+ if (!found) {
232
+ throw new Error(`Product healthcheck "${option.healthcheck}" not found. Register it first (e.g. npm run test:healthchecks:populate).`);
233
+ }
234
+ if (!option.check_interval) {
235
+ option.check_interval = 10000;
236
+ }
237
+ }
238
+ /** Keep in-memory product component arrays in sync after component CRUD (components API can lag). */
239
+ mergeProductComponent(kind, item) {
240
+ if (!this.product || !(item === null || item === void 0 ? void 0 : item.tag))
241
+ return;
242
+ const key = kind === 'healthcheck' ? 'healthchecks' : kind === 'quota' ? 'quota' : 'fallbacks';
243
+ const list = Array.isArray(this.product[key])
244
+ ? [...this.product[key]]
245
+ : [];
246
+ const idx = list.findIndex((c) => c.tag === item.tag);
247
+ if (idx >= 0)
248
+ list[idx] = Object.assign(Object.assign({}, list[idx]), item);
249
+ else
250
+ list.push(item);
251
+ this.product[key] = list;
252
+ }
218
253
  /**
219
254
  * Create a healthcheck from a schema (e.g. from resilience health.define/create).
220
255
  * Persists as product component so processor healthcheck workers and health.list/fetch see it.
@@ -1048,19 +1083,7 @@ class ProductsBuilderService {
1048
1083
  throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`);
1049
1084
  }
1050
1085
  if (d.healthcheck) {
1051
- const action = version.actions.find((action) => action.tag === d.healthcheck);
1052
- if (!d.check_interval) {
1053
- d.check_interval = 10000;
1054
- }
1055
- if (!action) {
1056
- throw new Error(`Cannot find healthcheck action ${d.healthcheck} on app ${app.tag} version ${app.version}`);
1057
- }
1058
- if (action.headers.data.length > 0 ||
1059
- action.body.data.length > 0 ||
1060
- action.params.data.length > 0 ||
1061
- action.query.data.length > 0) {
1062
- throw new Error('Healthcheck action is expected to have no headers, body, params or query');
1063
- }
1086
+ await this.validateProviderHealthcheckTag(d);
1064
1087
  }
1065
1088
  }
1066
1089
  d.last_checked = new Date();
@@ -1068,6 +1091,7 @@ class ProductsBuilderService {
1068
1091
  d.last_available = true;
1069
1092
  }));
1070
1093
  await this.productApi.updateProduct(this.product_id, Object.assign(Object.assign({}, data), { component: enums_1.ProductComponents.QUOTA, action: enums_1.RequestAction.CREATE }), this.getUserAccess());
1094
+ this.mergeProductComponent('quota', data);
1071
1095
  }
1072
1096
  }
1073
1097
  catch (e) {
@@ -1097,19 +1121,7 @@ class ProductsBuilderService {
1097
1121
  throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`);
1098
1122
  }
1099
1123
  if (d.healthcheck) {
1100
- const action = version.actions.find((action) => action.tag === d.healthcheck);
1101
- if (!d.check_interval) {
1102
- d.check_interval = 10000;
1103
- }
1104
- if (!action) {
1105
- throw new Error(`Cannot find healthcheck action ${d.healthcheck} on app ${app.tag} version ${app.version}`);
1106
- }
1107
- if (action.headers.data.length > 0 ||
1108
- action.body.data.length > 0 ||
1109
- action.params.data.length > 0 ||
1110
- action.query.data.length > 0) {
1111
- throw new Error('Healthcheck action is expected to have no headers, body, params or query');
1112
- }
1124
+ await this.validateProviderHealthcheckTag(d);
1113
1125
  }
1114
1126
  }
1115
1127
  d.last_checked = new Date();
@@ -1128,12 +1140,19 @@ class ProductsBuilderService {
1128
1140
  }
1129
1141
  }
1130
1142
  async fetchQuotas(version) {
1143
+ var _a;
1131
1144
  const components = await this.productApi.fetchProductComponents(this.product_id, 'quota', this.getUserAccess());
1132
- return components;
1145
+ if (Array.isArray(components) && components.length > 0) {
1146
+ return components;
1147
+ }
1148
+ return Array.isArray((_a = this.product) === null || _a === void 0 ? void 0 : _a.quota) ? this.product.quota : [];
1133
1149
  }
1134
1150
  async fetchQuota(tag, version) {
1151
+ var _a, _b, _c;
1135
1152
  const component = await this.productApi.fetchProductComponentByTag(this.product_id, 'quota', tag, this.getUserAccess());
1136
- return component;
1153
+ if (component)
1154
+ return component;
1155
+ return (_c = ((_b = (_a = this.product) === null || _a === void 0 ? void 0 : _a.quota) !== null && _b !== void 0 ? _b : []).find((q) => q.tag === tag)) !== null && _c !== void 0 ? _c : null;
1137
1156
  }
1138
1157
  async deleteQuota(tag) {
1139
1158
  try {
@@ -1171,19 +1190,7 @@ class ProductsBuilderService {
1171
1190
  throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`);
1172
1191
  }
1173
1192
  if (d.healthcheck) {
1174
- const action = version.actions.find((action) => action.tag === d.healthcheck);
1175
- if (!d.check_interval) {
1176
- d.check_interval = 10000;
1177
- }
1178
- if (!action) {
1179
- throw new Error(`Cannot find healthcheck action ${d.healthcheck} on app ${app.tag} version ${app.version}`);
1180
- }
1181
- if (action.headers.data.length > 0 ||
1182
- action.body.data.length > 0 ||
1183
- action.params.data.length > 0 ||
1184
- action.query.data.length > 0) {
1185
- throw new Error('Healthcheck action is expected to have no headers, body, params or query');
1186
- }
1193
+ await this.validateProviderHealthcheckTag(d);
1187
1194
  }
1188
1195
  }
1189
1196
  d.last_checked = new Date();
@@ -1191,6 +1198,7 @@ class ProductsBuilderService {
1191
1198
  d.last_available = true;
1192
1199
  }));
1193
1200
  await this.productApi.updateProduct(this.product_id, Object.assign(Object.assign({}, data), { component: enums_1.ProductComponents.FALLBACK, action: enums_1.RequestAction.CREATE }), this.getUserAccess());
1201
+ this.mergeProductComponent('fallback', data);
1194
1202
  }
1195
1203
  }
1196
1204
  catch (e) {
@@ -1217,19 +1225,7 @@ class ProductsBuilderService {
1217
1225
  throw new Error(`Cannot find action ${d.event} on app ${app.tag} version ${app.version}`);
1218
1226
  }
1219
1227
  if (d.healthcheck) {
1220
- const action = version.actions.find((action) => action.tag === d.healthcheck);
1221
- if (!d.check_interval) {
1222
- d.check_interval = 10000;
1223
- }
1224
- if (!action) {
1225
- throw new Error(`Cannot find healthcheck action ${d.healthcheck} on app ${app.tag} version ${app.version}`);
1226
- }
1227
- if (action.headers.data.length > 0 ||
1228
- action.body.data.length > 0 ||
1229
- action.params.data.length > 0 ||
1230
- action.query.data.length > 0) {
1231
- throw new Error('Healthcheck action is expected to have no headers, body, params or query');
1232
- }
1228
+ await this.validateProviderHealthcheckTag(d);
1233
1229
  }
1234
1230
  }
1235
1231
  d.last_checked = new Date();
@@ -1248,12 +1244,19 @@ class ProductsBuilderService {
1248
1244
  }
1249
1245
  }
1250
1246
  async fetchFallbacks(version) {
1247
+ var _a;
1251
1248
  const components = await this.productApi.fetchProductComponents(this.product_id, 'fallback', this.getUserAccess());
1252
- return components;
1249
+ if (Array.isArray(components) && components.length > 0) {
1250
+ return components;
1251
+ }
1252
+ return Array.isArray((_a = this.product) === null || _a === void 0 ? void 0 : _a.fallback) ? this.product.fallback : [];
1253
1253
  }
1254
1254
  async fetchFallback(tag, version) {
1255
+ var _a, _b, _c;
1255
1256
  const component = await this.productApi.fetchProductComponentByTag(this.product_id, 'fallback', tag, this.getUserAccess());
1256
- return component;
1257
+ if (component)
1258
+ return component;
1259
+ return (_c = ((_b = (_a = this.product) === null || _a === void 0 ? void 0 : _a.fallback) !== null && _b !== void 0 ? _b : []).find((f) => f.tag === tag)) !== null && _c !== void 0 ? _c : null;
1257
1260
  }
1258
1261
  async deleteFallback(tag) {
1259
1262
  try {
@@ -1347,6 +1350,19 @@ class ProductsBuilderService {
1347
1350
  const existingBroker = await this.fetchMessageBroker(data.tag);
1348
1351
  if (!existingBroker) {
1349
1352
  console.log(`${logPrefix} Broker ${data.tag} does not exist, proceeding with creation`);
1353
+ const auth = this.getUserAccess();
1354
+ const cloudApi = new cloudApi_service_1.CloudApiService(this.env_type);
1355
+ const productTag = this.product.tag;
1356
+ const materializedEnvs = [];
1357
+ for (const env of data.envs || []) {
1358
+ materializedEnvs.push(await (0, cloud_broker_link_util_1.materializeCloudLinkedBrokerEnv)(env, {
1359
+ cloudApi,
1360
+ auth,
1361
+ productTag,
1362
+ brokerTag: data.tag,
1363
+ }));
1364
+ }
1365
+ data.envs = materializedEnvs;
1350
1366
  console.log(`${logPrefix} Validating schema...`);
1351
1367
  await validators_1.CreateMessageBrokerSchema.validateAsync(data);
1352
1368
  console.log(`${logPrefix} Schema validation passed`);
@@ -1407,6 +1423,17 @@ class ProductsBuilderService {
1407
1423
  console.log(`${logPrefix} Existing broker found with ${((_b = messageBroker.envs) === null || _b === void 0 ? void 0 : _b.length) || 0} environments`);
1408
1424
  const { _id, envs } = messageBroker;
1409
1425
  console.log(`${logPrefix} Broker ID: ${_id}`);
1426
+ if (data.envs && data.envs.length > 0) {
1427
+ const auth = this.getUserAccess();
1428
+ const cloudApi = new cloudApi_service_1.CloudApiService(this.env_type);
1429
+ const productTag = this.product.tag;
1430
+ data.envs = await Promise.all(data.envs.map(async (env) => (0, cloud_broker_link_util_1.materializeCloudLinkedBrokerEnv)(env, {
1431
+ cloudApi,
1432
+ auth,
1433
+ productTag,
1434
+ brokerTag: data.tag || tag,
1435
+ })));
1436
+ }
1410
1437
  console.log(`${logPrefix} Validating update schema...`);
1411
1438
  await validators_1.UpdateMessageBrokerSchema.validateAsync(data);
1412
1439
  console.log(`${logPrefix} Schema validation passed`);
@@ -1415,7 +1442,7 @@ class ProductsBuilderService {
1415
1442
  throw new Error(`tag ${tag} is in use`);
1416
1443
  }
1417
1444
  const processedEnvs = [];
1418
- for (const env of data.envs) {
1445
+ for (const env of data.envs || []) {
1419
1446
  console.log(`${logPrefix} Processing env: ${env.slug} (type: ${env.type})`);
1420
1447
  const exists = await this.fetchEnv(env.slug);
1421
1448
  if (!exists) {
@@ -1570,6 +1597,19 @@ class ProductsBuilderService {
1570
1597
  }
1571
1598
  async createStorage(data, throwErrorIfExists = false) {
1572
1599
  if (!(await this.fetchStorage(data.tag))) {
1600
+ const auth = this.getUserAccess();
1601
+ const cloudApi = new cloudApi_service_1.CloudApiService(this.env_type);
1602
+ const productTag = this.product.tag;
1603
+ const materializedEnvs = [];
1604
+ for (const env of data.envs || []) {
1605
+ materializedEnvs.push(await (0, storage_cloud_link_util_1.materializeCloudLinkedStorageEnv)(env, {
1606
+ cloudApi,
1607
+ auth,
1608
+ productTag,
1609
+ storageTag: data.tag,
1610
+ }));
1611
+ }
1612
+ data.envs = materializedEnvs;
1573
1613
  await create_productStorage_validator_1.CreateProductStorageSchema.validateAsync(data);
1574
1614
  const processedEnvs = [];
1575
1615
  for (const env of data.envs) {
@@ -1608,11 +1648,22 @@ class ProductsBuilderService {
1608
1648
  throw new Error(`Storage ${tag} not found`);
1609
1649
  }
1610
1650
  const { _id, envs } = storage;
1611
- await create_productStorage_validator_1.UpdateProductStorageSchema.validateAsync(data); // Change to update;
1612
1651
  if (data.tag && await this.fetchStorage(data.tag)) {
1613
1652
  throw new Error(`tag ${tag} is in use`); // TODO: also check on the backend
1614
1653
  }
1615
1654
  // Only process envs if they are provided in the update
1655
+ if (data.envs && data.envs.length > 0) {
1656
+ const auth = this.getUserAccess();
1657
+ const cloudApi = new cloudApi_service_1.CloudApiService(this.env_type);
1658
+ const productTag = this.product.tag;
1659
+ data.envs = await Promise.all(data.envs.map(async (env) => (0, storage_cloud_link_util_1.materializeCloudLinkedStorageEnv)(env, {
1660
+ cloudApi,
1661
+ auth,
1662
+ productTag,
1663
+ storageTag: data.tag || tag,
1664
+ })));
1665
+ }
1666
+ await create_productStorage_validator_1.UpdateProductStorageSchema.validateAsync(data);
1616
1667
  if (data.envs && data.envs.length > 0) {
1617
1668
  data.envs = await Promise.all(data.envs.map(async (env) => {
1618
1669
  const exists = await this.fetchEnv(env.slug);
@@ -1725,6 +1776,19 @@ class ProductsBuilderService {
1725
1776
  productTag: (_b = this.product) === null || _b === void 0 ? void 0 : _b.tag,
1726
1777
  });
1727
1778
  if (!(await this.fetchVector(data.tag))) {
1779
+ const auth = this.getUserAccess();
1780
+ const cloudApi = new cloudApi_service_1.CloudApiService(this.env_type);
1781
+ const productTag = this.product.tag;
1782
+ const materializedEnvs = [];
1783
+ for (const env of data.envs || []) {
1784
+ materializedEnvs.push(await (0, cloud_vector_link_util_1.materializeCloudLinkedVectorEnv)(env, {
1785
+ cloudApi,
1786
+ auth,
1787
+ productTag,
1788
+ vectorTag: data.tag,
1789
+ }));
1790
+ }
1791
+ data.envs = materializedEnvs;
1728
1792
  await validators_1.CreateProductVectorSchema.validateAsync(data);
1729
1793
  const processedEnvs = [];
1730
1794
  for (const env of data.envs) {
@@ -1810,43 +1874,52 @@ class ProductsBuilderService {
1810
1874
  }
1811
1875
  if (data.envs) {
1812
1876
  console.log('[ProductsService.updateVector] Processing envs update');
1877
+ const auth = this.getUserAccess();
1878
+ const cloudApi = new cloudApi_service_1.CloudApiService(this.env_type);
1879
+ const productTag = this.product.tag;
1813
1880
  data.envs = await Promise.all(data.envs.map(async (env) => {
1814
1881
  var _a, _b;
1882
+ const materialized = await (0, cloud_vector_link_util_1.materializeCloudLinkedVectorEnv)(env, {
1883
+ cloudApi,
1884
+ auth,
1885
+ productTag,
1886
+ vectorTag: data.tag || tag,
1887
+ });
1815
1888
  console.log('[ProductsService.updateVector] Processing env:', {
1816
1889
  slug: env.slug,
1817
1890
  hasApiKey: !!env.apiKey,
1818
1891
  hasEndpoint: !!env.endpoint,
1819
1892
  });
1820
- const exists = await this.fetchEnv(env.slug);
1893
+ const exists = await this.fetchEnv(materialized.slug);
1821
1894
  if (!exists) {
1822
- throw new Error(`Env ${env.slug} does not exist`);
1895
+ throw new Error(`Env ${materialized.slug} does not exist`);
1823
1896
  }
1824
1897
  // Store API key as secret and encrypt
1825
- if (env.apiKey) {
1826
- console.log('[ProductsService.updateVector] Converting API key to secret for env:', env.slug);
1827
- const originalApiKey = env.apiKey;
1828
- env.apiKey = await this.storeVectorApiKeyAsSecret(env.apiKey, data.tag || tag, env.slug);
1898
+ if (materialized.apiKey) {
1899
+ console.log('[ProductsService.updateVector] Converting API key to secret for env:', materialized.slug);
1900
+ const originalApiKey = materialized.apiKey;
1901
+ materialized.apiKey = await this.storeVectorApiKeyAsSecret(materialized.apiKey, data.tag || tag, materialized.slug);
1829
1902
  console.log('[ProductsService.updateVector] API key after secret conversion:', {
1830
- wasConverted: originalApiKey !== env.apiKey,
1831
- isSecretRef: (_a = env.apiKey) === null || _a === void 0 ? void 0 : _a.startsWith('$Secret{'),
1903
+ wasConverted: originalApiKey !== materialized.apiKey,
1904
+ isSecretRef: (_a = materialized.apiKey) === null || _a === void 0 ? void 0 : _a.startsWith('$Secret{'),
1832
1905
  });
1833
- env.apiKey = (0, processor_utils_1.encrypt)(env.apiKey, this.product.private_key);
1906
+ materialized.apiKey = (0, processor_utils_1.encrypt)(materialized.apiKey, this.product.private_key);
1834
1907
  console.log('[ProductsService.updateVector] API key encrypted');
1835
1908
  }
1836
1909
  // Store endpoint as secret and encrypt
1837
- if (env.endpoint) {
1838
- console.log('[ProductsService.updateVector] Converting endpoint to secret for env:', env.slug);
1839
- const originalEndpoint = env.endpoint;
1840
- env.endpoint = await this.storeVectorEndpointAsSecret(env.endpoint, data.tag || tag, env.slug);
1910
+ if (materialized.endpoint) {
1911
+ console.log('[ProductsService.updateVector] Converting endpoint to secret for env:', materialized.slug);
1912
+ const originalEndpoint = materialized.endpoint;
1913
+ materialized.endpoint = await this.storeVectorEndpointAsSecret(materialized.endpoint, data.tag || tag, materialized.slug);
1841
1914
  console.log('[ProductsService.updateVector] Endpoint after secret conversion:', {
1842
- wasConverted: originalEndpoint !== env.endpoint,
1843
- isSecretRef: (_b = env.endpoint) === null || _b === void 0 ? void 0 : _b.startsWith('$Secret{'),
1915
+ wasConverted: originalEndpoint !== materialized.endpoint,
1916
+ isSecretRef: (_b = materialized.endpoint) === null || _b === void 0 ? void 0 : _b.startsWith('$Secret{'),
1844
1917
  });
1845
- env.endpoint = (0, processor_utils_1.encrypt)(env.endpoint, this.product.private_key);
1918
+ materialized.endpoint = (0, processor_utils_1.encrypt)(materialized.endpoint, this.product.private_key);
1846
1919
  console.log('[ProductsService.updateVector] Endpoint encrypted');
1847
1920
  }
1848
- console.log('[ProductsService.updateVector] Env processed successfully:', env.slug);
1849
- return env;
1921
+ console.log('[ProductsService.updateVector] Env processed successfully:', materialized.slug);
1922
+ return materialized;
1850
1923
  }));
1851
1924
  const overwrite = [];
1852
1925
  const newEnvs = [];
@@ -2527,18 +2600,26 @@ class ProductsBuilderService {
2527
2600
  }
2528
2601
  }
2529
2602
  async fetchAppWebhooks(access_tag) {
2603
+ var _a;
2530
2604
  try {
2531
2605
  const app = await this.fetchThirdPartyAppByAccessTag(access_tag);
2532
2606
  if (!app) {
2533
2607
  throw new Error(`App with access tag ${access_tag} not found`);
2534
2608
  }
2535
2609
  const version = app.versions.find((version) => version.tag === app.version);
2536
- //const status = await this.webhooksApi.fetchWebhooks({access_tag, app_tag: app.tag, version: app.version}, this.getUserAccess())
2537
- console.log(status);
2538
2610
  if (!version) {
2539
2611
  throw new Error(`Required app version not found`);
2540
2612
  }
2541
- return version.webhooks;
2613
+ try {
2614
+ const withConfig = await this.webhooksApi.fetchWebhooks({ access_tag, app_tag: app.tag, version: app.version }, this.getUserAccess());
2615
+ if (Array.isArray(withConfig) && withConfig.length > 0) {
2616
+ return withConfig;
2617
+ }
2618
+ }
2619
+ catch (_b) {
2620
+ // Fall back to app definition webhooks when runtime service is unavailable
2621
+ }
2622
+ return (_a = version.webhooks) !== null && _a !== void 0 ? _a : [];
2542
2623
  }
2543
2624
  catch (e) {
2544
2625
  throw e;
@@ -4017,11 +4098,11 @@ class ProductsBuilderService {
4017
4098
  return datapoint;
4018
4099
  }
4019
4100
  async fetchThirdPartyAppByAccessTag(access_tag) {
4101
+ const cached = this.thirdPartyApps.find((item) => access_tag === item.access_tag);
4102
+ if (cached) {
4103
+ return Object.assign(Object.assign({}, cached.app), { version: cached.version });
4104
+ }
4020
4105
  try {
4021
- const app = this.thirdPartyApps.find((item) => access_tag === item.access_tag);
4022
- if (app) {
4023
- return Object.assign(Object.assign({}, app.app), { version: app.version });
4024
- }
4025
4106
  const access = await this.appApi.fetchAccessByTag(access_tag, this.getUserAccess());
4026
4107
  if (!access) {
4027
4108
  throw new Error(`Access to app ${access_tag} not found`);
@@ -4030,10 +4111,57 @@ class ProductsBuilderService {
4030
4111
  this.thirdPartyApps.push({ access_tag, app: appData, version: access.version });
4031
4112
  return Object.assign(Object.assign({}, appData), { version: access.version });
4032
4113
  }
4033
- catch (e) {
4034
- throw e;
4114
+ catch (primaryError) {
4115
+ const resolved = await this.fetchThirdPartyAppByAppTagFallback(access_tag);
4116
+ if (resolved) {
4117
+ this.thirdPartyApps.push({
4118
+ access_tag,
4119
+ app: resolved,
4120
+ version: resolved.version,
4121
+ });
4122
+ return resolved;
4123
+ }
4124
+ throw primaryError;
4035
4125
  }
4036
4126
  }
4127
+ /**
4128
+ * When workspace access-tag lookup fails (common for product-scoped tags like ductape:paystack:ductape),
4129
+ * resolve the app via product-linked components or fetchAppByTag.
4130
+ */
4131
+ async fetchThirdPartyAppByAppTagFallback(access_tag) {
4132
+ var _a, _b, _c, _d, _e, _f, _g;
4133
+ const appTag = await this.resolveAppTagFromAccessReference(access_tag);
4134
+ if (!appTag)
4135
+ return null;
4136
+ const appData = await this.appApi.fetchAppByTag(appTag, this.getUserAccess());
4137
+ if (!appData)
4138
+ return null;
4139
+ const version = (_g = (_d = (_a = (typeof appData.version === 'string'
4140
+ ? appData.version
4141
+ : undefined)) !== null && _a !== void 0 ? _a : (_c = (_b = appData.versions) === null || _b === void 0 ? void 0 : _b.find((v) => v.tag === 'ductape')) === null || _c === void 0 ? void 0 : _c.tag) !== null && _d !== void 0 ? _d : (_f = (_e = appData.versions) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.tag) !== null && _g !== void 0 ? _g : 'ductape';
4142
+ return Object.assign(Object.assign({}, appData), { version });
4143
+ }
4144
+ async resolveAppTagFromAccessReference(access_tag) {
4145
+ if (this.product_id) {
4146
+ const productApps = (await this.productApi.fetchProductComponents(this.product_id, 'app', this.getUserAccess()));
4147
+ const linked = productApps.find((a) => {
4148
+ var _a;
4149
+ return a.access_tag === access_tag ||
4150
+ a.app_tag === access_tag ||
4151
+ ((_a = a.access_tag) === null || _a === void 0 ? void 0 : _a.startsWith(`${access_tag}:`));
4152
+ });
4153
+ if (linked === null || linked === void 0 ? void 0 : linked.app_tag)
4154
+ return linked.app_tag;
4155
+ }
4156
+ // Heuristic: product access tags are often `{app_tag}:{workspace}` — try app tag prefix
4157
+ if (access_tag.includes(':')) {
4158
+ const parts = access_tag.split(':');
4159
+ if (parts.length >= 2) {
4160
+ return `${parts[0]}:${parts[1]}`;
4161
+ }
4162
+ }
4163
+ return access_tag;
4164
+ }
4037
4165
  fetchThirdPartyAppActionByTag(actions, action_tag) {
4038
4166
  const action = actions.find((action) => action.tag === action_tag);
4039
4167
  if (!action) {
@@ -4107,8 +4235,20 @@ class ProductsBuilderService {
4107
4235
  }
4108
4236
  async createDatabase(data, throwErrorIfExists = false) {
4109
4237
  try {
4110
- // TODO: figure out a way to check if this has run before, halt if it has
4111
4238
  if (!(await this.fetchDatabase(data.tag))) {
4239
+ const auth = this.getUserAccess();
4240
+ const cloudApi = new cloudApi_service_1.CloudApiService(this.env_type);
4241
+ const productTag = this.product.tag;
4242
+ const materializedEnvs = [];
4243
+ for (const env of data.envs || []) {
4244
+ materializedEnvs.push(await (0, cloud_database_link_util_1.materializeCloudLinkedDatabaseEnv)(env, {
4245
+ cloudApi,
4246
+ auth,
4247
+ productTag,
4248
+ databaseTag: data.tag,
4249
+ }));
4250
+ }
4251
+ data.envs = materializedEnvs;
4112
4252
  await validators_1.CreateProductDatabaseSchema.validateAsync(data);
4113
4253
  const processedEnvs = [];
4114
4254
  for (const env of data.envs) {
@@ -4145,20 +4285,36 @@ class ProductsBuilderService {
4145
4285
  throw new Error(`Database ${tag} not found`);
4146
4286
  }
4147
4287
  const { _id, envs } = db;
4148
- await validators_1.UpdateProductDatabaseSchema.validateAsync(data); // Change to update;
4149
4288
  if (data.tag && this.fetchDatabase(data.tag)) {
4150
4289
  throw new Error(`tag ${tag} is in use`); // TODO: also check on the backend
4151
4290
  }
4152
- data.envs = await Promise.all(data.envs.map(async (env) => {
4153
- const exists = await this.fetchEnv(env.slug);
4154
- if (!exists) {
4155
- throw new Error(`Env ${env.slug} does not exist`);
4156
- }
4157
- if (env.connection_url) {
4158
- env.connection_url = (0, processor_utils_1.encrypt)(env.connection_url, this.product.private_key);
4159
- }
4160
- return env;
4161
- }));
4291
+ if (data.envs && data.envs.length > 0) {
4292
+ const auth = this.getUserAccess();
4293
+ const cloudApi = new cloudApi_service_1.CloudApiService(this.env_type);
4294
+ const productTag = this.product.tag;
4295
+ data.envs = await Promise.all(data.envs.map(async (env) => (0, cloud_database_link_util_1.materializeCloudLinkedDatabaseEnv)(env, {
4296
+ cloudApi,
4297
+ auth,
4298
+ productTag,
4299
+ databaseTag: data.tag || tag,
4300
+ })));
4301
+ }
4302
+ await validators_1.UpdateProductDatabaseSchema.validateAsync(data);
4303
+ if (data.envs && data.envs.length > 0) {
4304
+ data.envs = await Promise.all(data.envs.map(async (env) => {
4305
+ const exists = await this.fetchEnv(env.slug);
4306
+ if (!exists) {
4307
+ throw new Error(`Env ${env.slug} does not exist`);
4308
+ }
4309
+ if (env.connection_url) {
4310
+ env.connection_url = (0, processor_utils_1.encrypt)(env.connection_url, this.product.private_key);
4311
+ }
4312
+ return env;
4313
+ }));
4314
+ }
4315
+ if (!data.envs) {
4316
+ data.envs = [];
4317
+ }
4162
4318
  const overwrite = [];
4163
4319
  const newEnvs = [];
4164
4320
  data.envs.map((dataEnv) => {
@@ -4241,6 +4397,19 @@ class ProductsBuilderService {
4241
4397
  });
4242
4398
  try {
4243
4399
  if (!(await this.fetchGraph(data.tag))) {
4400
+ const auth = this.getUserAccess();
4401
+ const cloudApi = new cloudApi_service_1.CloudApiService(this.env_type);
4402
+ const productTag = this.product.tag;
4403
+ const materializedEnvs = [];
4404
+ for (const env of data.envs || []) {
4405
+ materializedEnvs.push(await (0, cloud_graph_link_util_1.materializeCloudLinkedGraphEnv)(env, {
4406
+ cloudApi,
4407
+ auth,
4408
+ productTag,
4409
+ graphTag: data.tag,
4410
+ }));
4411
+ }
4412
+ data.envs = materializedEnvs;
4244
4413
  await validators_1.CreateProductGraphSchema.validateAsync(data);
4245
4414
  const processedEnvs = [];
4246
4415
  for (const env of data.envs) {
@@ -4321,41 +4490,50 @@ class ProductsBuilderService {
4321
4490
  throw new Error(`tag ${tag} is in use`);
4322
4491
  }
4323
4492
  console.log('[ProductsService.updateGraph] Processing envs update');
4493
+ const auth = this.getUserAccess();
4494
+ const cloudApi = new cloudApi_service_1.CloudApiService(this.env_type);
4495
+ const productTag = this.product.tag;
4324
4496
  data.envs = await Promise.all(data.envs.map(async (env) => {
4325
4497
  var _a;
4498
+ const materialized = await (0, cloud_graph_link_util_1.materializeCloudLinkedGraphEnv)(env, {
4499
+ cloudApi,
4500
+ auth,
4501
+ productTag,
4502
+ graphTag: data.tag || tag,
4503
+ });
4326
4504
  console.log('[ProductsService.updateGraph] Processing env:', {
4327
- slug: env.slug,
4328
- hasConnectionUrl: !!env.connection_url,
4329
- hasUsername: !!env.username,
4330
- hasPassword: !!env.password,
4505
+ slug: materialized.slug,
4506
+ hasConnectionUrl: !!materialized.connection_url,
4507
+ hasUsername: !!materialized.username,
4508
+ hasPassword: !!materialized.password,
4331
4509
  });
4332
- const exists = await this.fetchEnv(env.slug);
4510
+ const exists = await this.fetchEnv(materialized.slug);
4333
4511
  if (!exists) {
4334
- throw new Error(`Env ${env.slug} does not exist`);
4512
+ throw new Error(`Env ${materialized.slug} does not exist`);
4335
4513
  }
4336
- if (env.connection_url) {
4514
+ if (materialized.connection_url) {
4337
4515
  // Store connection URL as secret
4338
- console.log('[ProductsService.updateGraph] Converting connection URL to secret for env:', env.slug);
4339
- const originalUrl = env.connection_url;
4340
- env.connection_url = await this.storeGraphConnectionUrlAsSecret(env.connection_url, data.tag || tag, env.slug);
4516
+ console.log('[ProductsService.updateGraph] Converting connection URL to secret for env:', materialized.slug);
4517
+ const originalUrl = materialized.connection_url;
4518
+ materialized.connection_url = await this.storeGraphConnectionUrlAsSecret(materialized.connection_url, data.tag || tag, materialized.slug);
4341
4519
  console.log('[ProductsService.updateGraph] Connection URL after secret conversion:', {
4342
- wasConverted: originalUrl !== env.connection_url,
4343
- isSecretRef: (_a = env.connection_url) === null || _a === void 0 ? void 0 : _a.startsWith('$Secret{'),
4520
+ wasConverted: originalUrl !== materialized.connection_url,
4521
+ isSecretRef: (_a = materialized.connection_url) === null || _a === void 0 ? void 0 : _a.startsWith('$Secret{'),
4344
4522
  });
4345
- env.connection_url = (0, processor_utils_1.encrypt)(env.connection_url, this.product.private_key);
4523
+ materialized.connection_url = (0, processor_utils_1.encrypt)(materialized.connection_url, this.product.private_key);
4346
4524
  console.log('[ProductsService.updateGraph] Connection URL encrypted');
4347
4525
  }
4348
4526
  // Encrypt username and password if provided
4349
- if (env.username) {
4350
- console.log('[ProductsService.updateGraph] Encrypting username for env:', env.slug);
4351
- env.username = (0, processor_utils_1.encrypt)(env.username, this.product.private_key);
4527
+ if (materialized.username) {
4528
+ console.log('[ProductsService.updateGraph] Encrypting username for env:', materialized.slug);
4529
+ materialized.username = (0, processor_utils_1.encrypt)(materialized.username, this.product.private_key);
4352
4530
  }
4353
- if (env.password) {
4354
- console.log('[ProductsService.updateGraph] Encrypting password for env:', env.slug);
4355
- env.password = (0, processor_utils_1.encrypt)(env.password, this.product.private_key);
4531
+ if (materialized.password) {
4532
+ console.log('[ProductsService.updateGraph] Encrypting password for env:', materialized.slug);
4533
+ materialized.password = (0, processor_utils_1.encrypt)(materialized.password, this.product.private_key);
4356
4534
  }
4357
- console.log('[ProductsService.updateGraph] Env processed successfully:', env.slug);
4358
- return env;
4535
+ console.log('[ProductsService.updateGraph] Env processed successfully:', materialized.slug);
4536
+ return materialized;
4359
4537
  }));
4360
4538
  const overwrite = [];
4361
4539
  const newEnvs = [];