@adobe/spacecat-shared-data-access 4.9.0 → 4.10.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,9 @@
1
+ ## [@adobe/spacecat-shared-data-access-v4.10.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v4.9.0...@adobe/spacecat-shared-data-access-v4.10.0) (2026-07-20)
2
+
3
+ ### Features
4
+
5
+ * Add allSiteIdsByTier and allByEnrollmentAndTier collection meth… ([#1569](https://github.com/adobe/spacecat-shared/issues/1569)) ([3ac585b](https://github.com/adobe/spacecat-shared/commit/3ac585bd1b3fe93d32b8d0ad027af81cf6e37830))
6
+
1
7
  ## [@adobe/spacecat-shared-data-access-v4.9.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v4.8.0...@adobe/spacecat-shared-data-access-v4.9.0) (2026-07-15)
2
8
 
3
9
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-data-access",
3
- "version": "4.9.0",
3
+ "version": "4.10.0",
4
4
  "description": "Shared modules of the Spacecat Services - Data Access",
5
5
  "type": "module",
6
6
  "engines": {
@@ -477,6 +477,16 @@ export const configSchema = Joi.object({
477
477
  storeCode: Joi.string().required(),
478
478
  storeViewCode: Joi.string().required(),
479
479
  hostName: Joi.string().optional(),
480
+ catalogFieldConfig: Joi.object({
481
+ name: Joi.object({
482
+ enabled: Joi.boolean().required(),
483
+ maxLength: Joi.number().integer().min(0).optional(),
484
+ }).optional(),
485
+ description: Joi.object({
486
+ enabled: Joi.boolean().required(),
487
+ maxLength: Joi.number().integer().min(0).optional(),
488
+ }).optional(),
489
+ }).optional(),
480
490
  }).options({ stripUnknown: true }),
481
491
  ).optional(),
482
492
  contentAiConfig: Joi.object({
@@ -409,6 +409,8 @@ export interface SiteCollection extends BaseCollection<Site> {
409
409
  allByProjectName(projectName: string): Promise<Site[]>;
410
410
  allByOrganizationIdAndProjectId(organizationId: string, projectId: string): Promise<Site[]>;
411
411
  allByOrganizationIdAndProjectName(organizationId: string, projectName: string): Promise<Site[]>;
412
+ allByEnrollmentProductCode(productCode: string, options?: object): Promise<Site[]>;
413
+ allByEnrollmentAndTier(tier: string, productCode?: string, options?: object): Promise<Site[]>;
412
414
  allSitesToAudit(): Promise<string[]>;
413
415
  allWithLatestAudit(auditType: string, order?: string, deliveryType?: string): Promise<Site[]>;
414
416
  findByBaseURL(baseURL: string): Promise<Site | null>;
@@ -174,6 +174,38 @@ class SiteCollection extends BaseCollection {
174
174
  return sites;
175
175
  }
176
176
 
177
+ /**
178
+ * Returns all sites enrolled at a given entitlement tier (e.g. 'PAID',
179
+ * 'FREE_TRIAL', 'PLG'). Optionally narrows the result to a single product
180
+ * code (e.g. 'LLMO').
181
+ *
182
+ * Uses entityRegistry to chain through SiteEnrollmentCollection, then
183
+ * batch-fetches full Site objects.
184
+ *
185
+ * @param {string} tier - Entitlement tier to filter by.
186
+ * @param {string} [productCode] - Optional product code to further filter by.
187
+ * @param {object} [options] - batchGetByKeys options (e.g. attribute projection).
188
+ * @returns {Promise<Site[]>}
189
+ */
190
+ async allByEnrollmentAndTier(tier, productCode, options = {}) {
191
+ if (!hasText(tier)) {
192
+ throw new DataAccessError('tier is required', this);
193
+ }
194
+
195
+ const siteEnrollmentCollection = this.entityRegistry.getCollection('SiteEnrollmentCollection');
196
+
197
+ const siteIds = await siteEnrollmentCollection.allSiteIdsByTier(tier, productCode);
198
+ if (siteIds.length === 0) {
199
+ return [];
200
+ }
201
+
202
+ const { data: sites } = await this.batchGetByKeys(
203
+ siteIds.map((siteId) => ({ siteId })),
204
+ options,
205
+ );
206
+ return sites;
207
+ }
208
+
177
209
  async allByOrganizationIdAndProjectName(organizationId, projectName) {
178
210
  if (!hasText(organizationId)) {
179
211
  throw new DataAccessError('organizationId is required', this);
@@ -27,6 +27,8 @@ export interface SiteEnrollmentCollection extends
27
27
  BaseCollection<SiteEnrollment> {
28
28
  allBySiteId(siteId: string): Promise<SiteEnrollment[]>;
29
29
  allByEntitlementId(entitlementId: string): Promise<SiteEnrollment[]>;
30
+ allSiteIdsByProductCode(productCode: string): Promise<string[]>;
31
+ allSiteIdsByTier(tier: string, productCode?: string): Promise<string[]>;
30
32
 
31
33
  findBySiteId(siteId: string): Promise<SiteEnrollment | null>;
32
34
  findByEntitlementId(entitlementId: string): Promise<SiteEnrollment | null>;
@@ -10,6 +10,7 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
+ import { hasText } from '@adobe/spacecat-shared-utils';
13
14
  import BaseCollection from '../base/base.collection.js';
14
15
  import DataAccessError from '../../errors/data-access.error.js';
15
16
 
@@ -47,6 +48,39 @@ class SiteEnrollmentCollection extends BaseCollection {
47
48
  return (data || []).map((row) => row.site_id);
48
49
  }
49
50
 
51
+ /**
52
+ * Returns all site IDs enrolled at a given entitlement tier (e.g. 'PAID',
53
+ * 'FREE_TRIAL', 'PLG') in a single JOIN query. Optionally narrows the tier
54
+ * match to a single product code.
55
+ *
56
+ * @param {string} tier - Entitlement tier to filter by.
57
+ * @param {string} [productCode] - Optional product code to further filter by.
58
+ * @returns {Promise<string[]>} Array of siteId strings.
59
+ */
60
+ async allSiteIdsByTier(tier, productCode) {
61
+ if (!hasText(tier)) {
62
+ throw new DataAccessError('tier is required', { entityName: 'SiteEnrollment', tableName: 'site_enrollments' });
63
+ }
64
+
65
+ let query = this.postgrestService
66
+ .from(this.tableName)
67
+ .select('site_id, entitlements!inner(tier, product_code)')
68
+ .eq('entitlements.tier', tier);
69
+
70
+ if (productCode) {
71
+ query = query.eq('entitlements.product_code', productCode);
72
+ }
73
+
74
+ const { data, error } = await query;
75
+
76
+ if (error) {
77
+ this.log.error(`[SiteEnrollmentCollection] Failed to query site_enrollments by tier - ${error.message}`, error);
78
+ throw new DataAccessError('Failed to query site_enrollments by tier', { entityName: 'SiteEnrollment', tableName: 'site_enrollments' }, error);
79
+ }
80
+
81
+ return [...new Set((data || []).map((row) => row.site_id))];
82
+ }
83
+
50
84
  async create(item, options = {}) {
51
85
  if (item?.siteId && item?.entitlementId) {
52
86
  const existing = await this.findByIndexKeys({