@adobe/spacecat-shared-data-access 3.7.0 → 3.9.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [@adobe/spacecat-shared-data-access-v3.9.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.8.0...@adobe/spacecat-shared-data-access-v3.9.0) (2026-03-05)
2
+
3
+ ### Features
4
+
5
+ * add optional aws region to cdn bucket config ([#1404](https://github.com/adobe/spacecat-shared/issues/1404)) ([76a7051](https://github.com/adobe/spacecat-shared/commit/76a7051f465d0d43cb03875fcc77b3ff174c4317))
6
+
7
+ ## [@adobe/spacecat-shared-data-access-v3.8.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.7.0...@adobe/spacecat-shared-data-access-v3.8.0) (2026-03-05)
8
+
9
+ ### Features
10
+
11
+ * **data-access:** add bulk entitlement query with organization embedding ([#1402](https://github.com/adobe/spacecat-shared/issues/1402)) ([588cfce](https://github.com/adobe/spacecat-shared/commit/588cfce8e1b4e81a55c3fbca4c7491368196b558))
12
+
1
13
  ## [@adobe/spacecat-shared-data-access-v3.7.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.6.2...@adobe/spacecat-shared-data-access-v3.7.0) (2026-03-04)
2
14
 
3
15
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-data-access",
3
- "version": "3.7.0",
3
+ "version": "3.9.0",
4
4
  "description": "Shared modules of the Spacecat Services - Data Access",
5
5
  "type": "module",
6
6
  "engines": {
@@ -11,6 +11,8 @@
11
11
  */
12
12
 
13
13
  import BaseCollection from '../base/base.collection.js';
14
+ import DataAccessError from '../../errors/data-access.error.js';
15
+ import { DEFAULT_PAGE_SIZE } from '../../util/postgrest.utils.js';
14
16
 
15
17
  /**
16
18
  * EntitlementCollection - A collection class responsible for managing Entitlement entities.
@@ -22,7 +24,67 @@ import BaseCollection from '../base/base.collection.js';
22
24
  class EntitlementCollection extends BaseCollection {
23
25
  static COLLECTION_NAME = 'EntitlementCollection';
24
26
 
25
- // add custom methods here
27
+ /**
28
+ * Finds all entitlements for a given product code with their parent organization
29
+ * data embedded via PostgREST resource embedding (INNER JOIN). This avoids N+1
30
+ * queries when you need both entitlement and organization data.
31
+ *
32
+ * Returns plain objects, not model instances, since the result combines fields
33
+ * from two entities. Callers should access properties directly
34
+ * (e.g., `result.entitlement.tier`), not via getter methods.
35
+ *
36
+ * @param {string} productCode - Product code to filter by (e.g., 'LLMO').
37
+ * @returns {Promise<Array<{entitlement: {id: string, productCode: string, tier: string},
38
+ * organization: {id: string, name: string, imsOrgId: string}}>>}
39
+ */
40
+ async allByProductCodeWithOrganization(productCode) {
41
+ if (!productCode) {
42
+ throw new DataAccessError('productCode is required', { entityName: 'Entitlement', tableName: 'entitlements' });
43
+ }
44
+
45
+ const allResults = [];
46
+ let offset = 0;
47
+ let keepGoing = true;
48
+
49
+ while (keepGoing) {
50
+ // eslint-disable-next-line no-await-in-loop
51
+ const { data, error } = await this.postgrestService
52
+ .from('entitlements')
53
+ .select('id, product_code, tier, organizations!inner(id, name, ims_org_id)')
54
+ .eq('product_code', productCode)
55
+ .range(offset, offset + DEFAULT_PAGE_SIZE - 1);
56
+
57
+ if (error) {
58
+ this.log.error(`[Entitlement] Failed to query entitlements with organizations - ${error.message}`, error);
59
+ throw new DataAccessError(
60
+ 'Failed to query entitlements with organizations',
61
+ { entityName: 'Entitlement', tableName: 'entitlements' },
62
+ error,
63
+ );
64
+ }
65
+
66
+ if (!data || data.length === 0) {
67
+ keepGoing = false;
68
+ } else {
69
+ allResults.push(...data);
70
+ keepGoing = data.length >= DEFAULT_PAGE_SIZE;
71
+ offset += DEFAULT_PAGE_SIZE;
72
+ }
73
+ }
74
+
75
+ return allResults.map((row) => ({
76
+ entitlement: {
77
+ id: row.id,
78
+ productCode: row.product_code,
79
+ tier: row.tier,
80
+ },
81
+ organization: {
82
+ id: row.organizations.id,
83
+ name: row.organizations.name,
84
+ imsOrgId: row.organizations.ims_org_id,
85
+ },
86
+ }));
87
+ }
26
88
  }
27
89
 
28
90
  export default EntitlementCollection;
@@ -31,6 +31,19 @@ export interface Entitlement extends BaseModel {
31
31
  setQuotas(quotas: object): Entitlement;
32
32
  }
33
33
 
34
+ export interface EntitlementWithOrganization {
35
+ entitlement: {
36
+ id: string;
37
+ productCode: EntitlementProductCode;
38
+ tier: EntitlementTier;
39
+ };
40
+ organization: {
41
+ id: string;
42
+ name: string;
43
+ imsOrgId: string;
44
+ } | null;
45
+ }
46
+
34
47
  export interface EntitlementCollection extends
35
48
  BaseCollection<Entitlement> {
36
49
  allByOrganizationId(organizationId: string): Promise<Entitlement[]>;
@@ -44,4 +57,8 @@ export interface EntitlementCollection extends
44
57
  organizationId: string,
45
58
  productCode: EntitlementProductCode,
46
59
  ): Promise<Entitlement | null>;
60
+
61
+ allByProductCodeWithOrganization(
62
+ productCode: EntitlementProductCode,
63
+ ): Promise<EntitlementWithOrganization[]>;
47
64
  }
@@ -46,6 +46,7 @@ export const IMPORT_SOURCES = {
46
46
  };
47
47
 
48
48
  const LLMO_TAG_PATTERN = /^(market|product|topic):\s?.+/;
49
+ const AWS_REGION_PATTERN = /^[a-z]{2}(?:-[a-z]+)+-\d+$/i;
49
50
  const LLMO_TAG = Joi.alternatives()
50
51
  .try(
51
52
  // Tag market, product, topic like this: "market: ch", "product: firefly", "topic: copyright"
@@ -324,6 +325,7 @@ export const configSchema = Joi.object({
324
325
  bucketName: Joi.string().optional(),
325
326
  orgId: Joi.string().optional(),
326
327
  cdnProvider: Joi.string().optional(),
328
+ region: Joi.string().pattern(AWS_REGION_PATTERN).optional(),
327
329
  }).optional(),
328
330
  }).optional(),
329
331
  cdnLogsConfig: Joi.object({